965e42308c307329d1b6b7a7c0e9c3599d455a81
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 /* for DigField() */
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 /* for MovePlayer() */
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 /* for ScrollPlayer() */
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 /* for Bang()/Explode() */
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 void InitGameControlValues()
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 void UpdatePlayfieldElementCount()
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 void UpdateGameControlValues()
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   /* used instead of "level_nr" (for network games) */
2228   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2229   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230
2231   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2232   for (i = 0; i < MAX_NUM_KEYS; i++)
2233     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2235   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236
2237   if (game.centered_player_nr == -1)
2238   {
2239     for (i = 0; i < MAX_PLAYERS; i++)
2240     {
2241       /* only one player in Supaplex game engine */
2242       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243         break;
2244
2245       for (k = 0; k < MAX_NUM_KEYS; k++)
2246       {
2247         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248         {
2249           if (level.native_em_level->ply[i]->keys & (1 << k))
2250             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251               get_key_element_from_nr(k);
2252         }
2253         else if (stored_player[i].key[k])
2254           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255             get_key_element_from_nr(k);
2256       }
2257
2258       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259         getPlayerInventorySize(i);
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287       getPlayerInventorySize(player_nr);
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   if (level.time == 0)
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315   else
2316     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317
2318   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2319   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407
2408   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2409     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2413       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2414        game.panel.element[i].id : EL_UNDEFINED);
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2418       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2419        element_info[game.panel.element_count[i].id].element_count : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2424        element_info[game.panel.ce_score[i].id].collect_score : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2429        element_info[game.panel.ce_score_element[i].id].collect_score :
2430        EL_UNDEFINED);
2431
2432   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2434   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435
2436   /* update game panel control frames */
2437
2438   for (i = 0; game_panel_controls[i].nr != -1; i++)
2439   {
2440     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441
2442     if (gpc->type == TYPE_ELEMENT)
2443     {
2444       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445       {
2446         int last_anim_random_frame = gfx.anim_random_frame;
2447         int element = gpc->value;
2448         int graphic = el2panelimg(element);
2449
2450         if (gpc->value != gpc->last_value)
2451         {
2452           gpc->gfx_frame = 0;
2453           gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455         else
2456         {
2457           gpc->gfx_frame++;
2458
2459           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2460               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2461             gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463
2464         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2465           gfx.anim_random_frame = gpc->gfx_random;
2466
2467         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2468           gpc->gfx_frame = element_info[element].collect_score;
2469
2470         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471                                               gpc->gfx_frame);
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = last_anim_random_frame;
2475       }
2476     }
2477     else if (gpc->type == TYPE_GRAPHIC)
2478     {
2479       if (gpc->graphic != IMG_UNDEFINED)
2480       {
2481         int last_anim_random_frame = gfx.anim_random_frame;
2482         int graphic = gpc->graphic;
2483
2484         if (gpc->value != gpc->last_value)
2485         {
2486           gpc->gfx_frame = 0;
2487           gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489         else
2490         {
2491           gpc->gfx_frame++;
2492
2493           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2494               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2495             gpc->gfx_random = INIT_GFX_RANDOM();
2496         }
2497
2498         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2499           gfx.anim_random_frame = gpc->gfx_random;
2500
2501         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = last_anim_random_frame;
2505       }
2506     }
2507   }
2508 }
2509
2510 void DisplayGameControlValues()
2511 {
2512   boolean redraw_panel = FALSE;
2513   int i;
2514
2515   for (i = 0; game_panel_controls[i].nr != -1; i++)
2516   {
2517     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518
2519     if (PANEL_DEACTIVATED(gpc->pos))
2520       continue;
2521
2522     if (gpc->value == gpc->last_value &&
2523         gpc->frame == gpc->last_frame)
2524       continue;
2525
2526     redraw_panel = TRUE;
2527   }
2528
2529   if (!redraw_panel)
2530     return;
2531
2532   /* copy default game door content to main double buffer */
2533
2534   /* !!! CHECK AGAIN !!! */
2535   SetPanelBackground();
2536   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2537   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538
2539   /* redraw game control buttons */
2540   RedrawGameButtons();
2541
2542   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543
2544   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545   {
2546     int nr = game_panel_order[i].nr;
2547     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2548     struct TextPosInfo *pos = gpc->pos;
2549     int type = gpc->type;
2550     int value = gpc->value;
2551     int frame = gpc->frame;
2552     int size = pos->size;
2553     int font = pos->font;
2554     boolean draw_masked = pos->draw_masked;
2555     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556
2557     if (PANEL_DEACTIVATED(pos))
2558       continue;
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563     if (type == TYPE_INTEGER)
2564     {
2565       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2566           nr == GAME_PANEL_TIME)
2567       {
2568         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569
2570         if (use_dynamic_size)           /* use dynamic number of digits */
2571         {
2572           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2573           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2574           int size2 = size1 + 1;
2575           int font1 = pos->font;
2576           int font2 = pos->font_alt;
2577
2578           size = (value < value_change ? size1 : size2);
2579           font = (value < value_change ? font1 : font2);
2580         }
2581       }
2582
2583       /* correct text size if "digits" is zero or less */
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       /* dynamically correct text alignment */
2588       pos->width = size * getFontWidth(font);
2589
2590       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                   int2str(value, size), font, mask_mode);
2592     }
2593     else if (type == TYPE_ELEMENT)
2594     {
2595       int element, graphic;
2596       Bitmap *src_bitmap;
2597       int src_x, src_y;
2598       int width, height;
2599       int dst_x = PANEL_XPOS(pos);
2600       int dst_y = PANEL_YPOS(pos);
2601
2602       if (value != EL_UNDEFINED && value != EL_EMPTY)
2603       {
2604         element = value;
2605         graphic = el2panelimg(value);
2606
2607         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608
2609         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610           size = TILESIZE;
2611
2612         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613                               &src_x, &src_y);
2614
2615         width  = graphic_info[graphic].width  * size / TILESIZE;
2616         height = graphic_info[graphic].height * size / TILESIZE;
2617
2618         if (draw_masked)
2619           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620                            dst_x, dst_y);
2621         else
2622           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2623                      dst_x, dst_y);
2624       }
2625     }
2626     else if (type == TYPE_GRAPHIC)
2627     {
2628       int graphic        = gpc->graphic;
2629       int graphic_active = gpc->graphic_active;
2630       Bitmap *src_bitmap;
2631       int src_x, src_y;
2632       int width, height;
2633       int dst_x = PANEL_XPOS(pos);
2634       int dst_y = PANEL_YPOS(pos);
2635       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2636                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637
2638       if (graphic != IMG_UNDEFINED && !skip)
2639       {
2640         if (pos->style == STYLE_REVERSE)
2641           value = 100 - value;
2642
2643         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644
2645         if (pos->direction & MV_HORIZONTAL)
2646         {
2647           width  = graphic_info[graphic_active].width * value / 100;
2648           height = graphic_info[graphic_active].height;
2649
2650           if (pos->direction == MV_LEFT)
2651           {
2652             src_x += graphic_info[graphic_active].width - width;
2653             dst_x += graphic_info[graphic_active].width - width;
2654           }
2655         }
2656         else
2657         {
2658           width  = graphic_info[graphic_active].width;
2659           height = graphic_info[graphic_active].height * value / 100;
2660
2661           if (pos->direction == MV_UP)
2662           {
2663             src_y += graphic_info[graphic_active].height - height;
2664             dst_y += graphic_info[graphic_active].height - height;
2665           }
2666         }
2667
2668         if (draw_masked)
2669           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670                            dst_x, dst_y);
2671         else
2672           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673                      dst_x, dst_y);
2674
2675         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676
2677         if (pos->direction & MV_HORIZONTAL)
2678         {
2679           if (pos->direction == MV_RIGHT)
2680           {
2681             src_x += width;
2682             dst_x += width;
2683           }
2684           else
2685           {
2686             dst_x = PANEL_XPOS(pos);
2687           }
2688
2689           width = graphic_info[graphic].width - width;
2690         }
2691         else
2692         {
2693           if (pos->direction == MV_DOWN)
2694           {
2695             src_y += height;
2696             dst_y += height;
2697           }
2698           else
2699           {
2700             dst_y = PANEL_YPOS(pos);
2701           }
2702
2703           height = graphic_info[graphic].height - height;
2704         }
2705
2706         if (draw_masked)
2707           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708                            dst_x, dst_y);
2709         else
2710           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2711                      dst_x, dst_y);
2712       }
2713     }
2714     else if (type == TYPE_STRING)
2715     {
2716       boolean active = (value != 0);
2717       char *state_normal = "off";
2718       char *state_active = "on";
2719       char *state = (active ? state_active : state_normal);
2720       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2721                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2722                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2723                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2724
2725       if (nr == GAME_PANEL_GRAVITY_STATE)
2726       {
2727         int font1 = pos->font;          /* (used for normal state) */
2728         int font2 = pos->font_alt;      /* (used for active state) */
2729
2730         font = (active ? font2 : font1);
2731       }
2732
2733       if (s != NULL)
2734       {
2735         char *s_cut;
2736
2737         if (size <= 0)
2738         {
2739           /* don't truncate output if "chars" is zero or less */
2740           size = strlen(s);
2741
2742           /* dynamically correct text alignment */
2743           pos->width = size * getFontWidth(font);
2744         }
2745
2746         s_cut = getStringCopyN(s, size);
2747
2748         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2749                     s_cut, font, mask_mode);
2750
2751         free(s_cut);
2752       }
2753     }
2754
2755     redraw_mask |= REDRAW_DOOR_1;
2756   }
2757
2758   SetGameStatus(GAME_MODE_PLAYING);
2759 }
2760
2761 void UpdateAndDisplayGameControlValues()
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 void UpdateGameDoorValues()
2771 {
2772   UpdateGameControlValues();
2773 }
2774
2775 void DrawGameDoorValues()
2776 {
2777   DisplayGameControlValues();
2778 }
2779
2780
2781 /*
2782   =============================================================================
2783   InitGameEngine()
2784   -----------------------------------------------------------------------------
2785   initialize game engine due to level / tape version number
2786   =============================================================================
2787 */
2788
2789 static void InitGameEngine()
2790 {
2791   int i, j, k, l, x, y;
2792
2793   /* set game engine from tape file when re-playing, else from level file */
2794   game.engine_version = (tape.playing ? tape.engine_version :
2795                          level.game_version);
2796
2797   /* set single or multi-player game mode (needed for re-playing tapes) */
2798   game.team_mode = setup.team_mode;
2799
2800   if (tape.playing)
2801   {
2802     int num_players = 0;
2803
2804     for (i = 0; i < MAX_PLAYERS; i++)
2805       if (tape.player_participates[i])
2806         num_players++;
2807
2808     /* multi-player tapes contain input data for more than one player */
2809     game.team_mode = (num_players > 1);
2810   }
2811
2812   /* ---------------------------------------------------------------------- */
2813   /* set flags for bugs and changes according to active game engine version */
2814   /* ---------------------------------------------------------------------- */
2815
2816   /*
2817     Summary of bugfix/change:
2818     Fixed handling for custom elements that change when pushed by the player.
2819
2820     Fixed/changed in version:
2821     3.1.0
2822
2823     Description:
2824     Before 3.1.0, custom elements that "change when pushing" changed directly
2825     after the player started pushing them (until then handled in "DigField()").
2826     Since 3.1.0, these custom elements are not changed until the "pushing"
2827     move of the element is finished (now handled in "ContinueMoving()").
2828
2829     Affected levels/tapes:
2830     The first condition is generally needed for all levels/tapes before version
2831     3.1.0, which might use the old behaviour before it was changed; known tapes
2832     that are affected are some tapes from the level set "Walpurgis Gardens" by
2833     Jamie Cullen.
2834     The second condition is an exception from the above case and is needed for
2835     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2836     above (including some development versions of 3.1.0), but before it was
2837     known that this change would break tapes like the above and was fixed in
2838     3.1.1, so that the changed behaviour was active although the engine version
2839     while recording maybe was before 3.1.0. There is at least one tape that is
2840     affected by this exception, which is the tape for the one-level set "Bug
2841     Machine" by Juergen Bonhagen.
2842   */
2843
2844   game.use_change_when_pushing_bug =
2845     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2846      !(tape.playing &&
2847        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2848        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2849
2850   /*
2851     Summary of bugfix/change:
2852     Fixed handling for blocking the field the player leaves when moving.
2853
2854     Fixed/changed in version:
2855     3.1.1
2856
2857     Description:
2858     Before 3.1.1, when "block last field when moving" was enabled, the field
2859     the player is leaving when moving was blocked for the time of the move,
2860     and was directly unblocked afterwards. This resulted in the last field
2861     being blocked for exactly one less than the number of frames of one player
2862     move. Additionally, even when blocking was disabled, the last field was
2863     blocked for exactly one frame.
2864     Since 3.1.1, due to changes in player movement handling, the last field
2865     is not blocked at all when blocking is disabled. When blocking is enabled,
2866     the last field is blocked for exactly the number of frames of one player
2867     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2868     last field is blocked for exactly one more than the number of frames of
2869     one player move.
2870
2871     Affected levels/tapes:
2872     (!!! yet to be determined -- probably many !!!)
2873   */
2874
2875   game.use_block_last_field_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,1,0));
2877
2878   game_em.use_single_button =
2879     (game.engine_version > VERSION_IDENT(4,0,0,2));
2880
2881   game_em.use_snap_key_bug =
2882     (game.engine_version < VERSION_IDENT(4,0,1,0));
2883
2884   /* ---------------------------------------------------------------------- */
2885
2886   /* set maximal allowed number of custom element changes per game frame */
2887   game.max_num_changes_per_frame = 1;
2888
2889   /* default scan direction: scan playfield from top/left to bottom/right */
2890   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2891
2892   /* dynamically adjust element properties according to game engine version */
2893   InitElementPropertiesEngine(game.engine_version);
2894
2895 #if 0
2896   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2897   printf("          tape version == %06d [%s] [file: %06d]\n",
2898          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2899          tape.file_version);
2900   printf("       => game.engine_version == %06d\n", game.engine_version);
2901 #endif
2902
2903   /* ---------- initialize player's initial move delay --------------------- */
2904
2905   /* dynamically adjust player properties according to level information */
2906   for (i = 0; i < MAX_PLAYERS; i++)
2907     game.initial_move_delay_value[i] =
2908       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2909
2910   /* dynamically adjust player properties according to game engine version */
2911   for (i = 0; i < MAX_PLAYERS; i++)
2912     game.initial_move_delay[i] =
2913       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2914        game.initial_move_delay_value[i] : 0);
2915
2916   /* ---------- initialize player's initial push delay --------------------- */
2917
2918   /* dynamically adjust player properties according to game engine version */
2919   game.initial_push_delay_value =
2920     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2921
2922   /* ---------- initialize changing elements ------------------------------- */
2923
2924   /* initialize changing elements information */
2925   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2926   {
2927     struct ElementInfo *ei = &element_info[i];
2928
2929     /* this pointer might have been changed in the level editor */
2930     ei->change = &ei->change_page[0];
2931
2932     if (!IS_CUSTOM_ELEMENT(i))
2933     {
2934       ei->change->target_element = EL_EMPTY_SPACE;
2935       ei->change->delay_fixed = 0;
2936       ei->change->delay_random = 0;
2937       ei->change->delay_frames = 1;
2938     }
2939
2940     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2941     {
2942       ei->has_change_event[j] = FALSE;
2943
2944       ei->event_page_nr[j] = 0;
2945       ei->event_page[j] = &ei->change_page[0];
2946     }
2947   }
2948
2949   /* add changing elements from pre-defined list */
2950   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2951   {
2952     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2953     struct ElementInfo *ei = &element_info[ch_delay->element];
2954
2955     ei->change->target_element       = ch_delay->target_element;
2956     ei->change->delay_fixed          = ch_delay->change_delay;
2957
2958     ei->change->pre_change_function  = ch_delay->pre_change_function;
2959     ei->change->change_function      = ch_delay->change_function;
2960     ei->change->post_change_function = ch_delay->post_change_function;
2961
2962     ei->change->can_change = TRUE;
2963     ei->change->can_change_or_has_action = TRUE;
2964
2965     ei->has_change_event[CE_DELAY] = TRUE;
2966
2967     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2968     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2969   }
2970
2971   /* ---------- initialize internal run-time variables --------------------- */
2972
2973   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2974   {
2975     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2976
2977     for (j = 0; j < ei->num_change_pages; j++)
2978     {
2979       ei->change_page[j].can_change_or_has_action =
2980         (ei->change_page[j].can_change |
2981          ei->change_page[j].has_action);
2982     }
2983   }
2984
2985   /* add change events from custom element configuration */
2986   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2987   {
2988     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2989
2990     for (j = 0; j < ei->num_change_pages; j++)
2991     {
2992       if (!ei->change_page[j].can_change_or_has_action)
2993         continue;
2994
2995       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2996       {
2997         /* only add event page for the first page found with this event */
2998         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2999         {
3000           ei->has_change_event[k] = TRUE;
3001
3002           ei->event_page_nr[k] = j;
3003           ei->event_page[k] = &ei->change_page[j];
3004         }
3005       }
3006     }
3007   }
3008
3009   /* ---------- initialize reference elements in change conditions --------- */
3010
3011   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3012   {
3013     int element = EL_CUSTOM_START + i;
3014     struct ElementInfo *ei = &element_info[element];
3015
3016     for (j = 0; j < ei->num_change_pages; j++)
3017     {
3018       int trigger_element = ei->change_page[j].initial_trigger_element;
3019
3020       if (trigger_element >= EL_PREV_CE_8 &&
3021           trigger_element <= EL_NEXT_CE_8)
3022         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3023
3024       ei->change_page[j].trigger_element = trigger_element;
3025     }
3026   }
3027
3028   /* ---------- initialize run-time trigger player and element ------------- */
3029
3030   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3031   {
3032     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3033
3034     for (j = 0; j < ei->num_change_pages; j++)
3035     {
3036       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3037       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3038       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3039       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3040       ei->change_page[j].actual_trigger_ce_value = 0;
3041       ei->change_page[j].actual_trigger_ce_score = 0;
3042     }
3043   }
3044
3045   /* ---------- initialize trigger events ---------------------------------- */
3046
3047   /* initialize trigger events information */
3048   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3049     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3050       trigger_events[i][j] = FALSE;
3051
3052   /* add trigger events from element change event properties */
3053   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3054   {
3055     struct ElementInfo *ei = &element_info[i];
3056
3057     for (j = 0; j < ei->num_change_pages; j++)
3058     {
3059       if (!ei->change_page[j].can_change_or_has_action)
3060         continue;
3061
3062       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3063       {
3064         int trigger_element = ei->change_page[j].trigger_element;
3065
3066         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3067         {
3068           if (ei->change_page[j].has_event[k])
3069           {
3070             if (IS_GROUP_ELEMENT(trigger_element))
3071             {
3072               struct ElementGroupInfo *group =
3073                 element_info[trigger_element].group;
3074
3075               for (l = 0; l < group->num_elements_resolved; l++)
3076                 trigger_events[group->element_resolved[l]][k] = TRUE;
3077             }
3078             else if (trigger_element == EL_ANY_ELEMENT)
3079               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3080                 trigger_events[l][k] = TRUE;
3081             else
3082               trigger_events[trigger_element][k] = TRUE;
3083           }
3084         }
3085       }
3086     }
3087   }
3088
3089   /* ---------- initialize push delay -------------------------------------- */
3090
3091   /* initialize push delay values to default */
3092   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3093   {
3094     if (!IS_CUSTOM_ELEMENT(i))
3095     {
3096       /* set default push delay values (corrected since version 3.0.7-1) */
3097       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3098       {
3099         element_info[i].push_delay_fixed = 2;
3100         element_info[i].push_delay_random = 8;
3101       }
3102       else
3103       {
3104         element_info[i].push_delay_fixed = 8;
3105         element_info[i].push_delay_random = 8;
3106       }
3107     }
3108   }
3109
3110   /* set push delay value for certain elements from pre-defined list */
3111   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3112   {
3113     int e = push_delay_list[i].element;
3114
3115     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3116     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3117   }
3118
3119   /* set push delay value for Supaplex elements for newer engine versions */
3120   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3121   {
3122     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3123     {
3124       if (IS_SP_ELEMENT(i))
3125       {
3126         /* set SP push delay to just enough to push under a falling zonk */
3127         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3128
3129         element_info[i].push_delay_fixed  = delay;
3130         element_info[i].push_delay_random = 0;
3131       }
3132     }
3133   }
3134
3135   /* ---------- initialize move stepsize ----------------------------------- */
3136
3137   /* initialize move stepsize values to default */
3138   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3139     if (!IS_CUSTOM_ELEMENT(i))
3140       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3141
3142   /* set move stepsize value for certain elements from pre-defined list */
3143   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3144   {
3145     int e = move_stepsize_list[i].element;
3146
3147     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3148   }
3149
3150   /* ---------- initialize collect score ----------------------------------- */
3151
3152   /* initialize collect score values for custom elements from initial value */
3153   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3154     if (IS_CUSTOM_ELEMENT(i))
3155       element_info[i].collect_score = element_info[i].collect_score_initial;
3156
3157   /* ---------- initialize collect count ----------------------------------- */
3158
3159   /* initialize collect count values for non-custom elements */
3160   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3161     if (!IS_CUSTOM_ELEMENT(i))
3162       element_info[i].collect_count_initial = 0;
3163
3164   /* add collect count values for all elements from pre-defined list */
3165   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3166     element_info[collect_count_list[i].element].collect_count_initial =
3167       collect_count_list[i].count;
3168
3169   /* ---------- initialize access direction -------------------------------- */
3170
3171   /* initialize access direction values to default (access from every side) */
3172   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3173     if (!IS_CUSTOM_ELEMENT(i))
3174       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3175
3176   /* set access direction value for certain elements from pre-defined list */
3177   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3178     element_info[access_direction_list[i].element].access_direction =
3179       access_direction_list[i].direction;
3180
3181   /* ---------- initialize explosion content ------------------------------- */
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183   {
3184     if (IS_CUSTOM_ELEMENT(i))
3185       continue;
3186
3187     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3188     {
3189       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3190
3191       element_info[i].content.e[x][y] =
3192         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3193          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3194          i == EL_PLAYER_3 ? EL_EMERALD :
3195          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3196          i == EL_MOLE ? EL_EMERALD_RED :
3197          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3198          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3199          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3200          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3201          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3202          i == EL_WALL_EMERALD ? EL_EMERALD :
3203          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3204          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3205          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3206          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3207          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3208          i == EL_WALL_PEARL ? EL_PEARL :
3209          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3210          EL_EMPTY);
3211     }
3212   }
3213
3214   /* ---------- initialize recursion detection ------------------------------ */
3215   recursion_loop_depth = 0;
3216   recursion_loop_detected = FALSE;
3217   recursion_loop_element = EL_UNDEFINED;
3218
3219   /* ---------- initialize graphics engine ---------------------------------- */
3220   game.scroll_delay_value =
3221     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3222      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3223   game.scroll_delay_value =
3224     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3225
3226   /* ---------- initialize game engine snapshots ---------------------------- */
3227   for (i = 0; i < MAX_PLAYERS; i++)
3228     game.snapshot.last_action[i] = 0;
3229   game.snapshot.changed_action = FALSE;
3230   game.snapshot.collected_item = FALSE;
3231   game.snapshot.mode =
3232     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3233      SNAPSHOT_MODE_EVERY_STEP :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3235      SNAPSHOT_MODE_EVERY_MOVE :
3236      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3237      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3238   game.snapshot.save_snapshot = FALSE;
3239
3240   /* ---------- initialize level time for Supaplex engine ------------------- */
3241   /* Supaplex levels with time limit currently unsupported -- should be added */
3242   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3243     level.time = 0;
3244 }
3245
3246 int get_num_special_action(int element, int action_first, int action_last)
3247 {
3248   int num_special_action = 0;
3249   int i, j;
3250
3251   for (i = action_first; i <= action_last; i++)
3252   {
3253     boolean found = FALSE;
3254
3255     for (j = 0; j < NUM_DIRECTIONS; j++)
3256       if (el_act_dir2img(element, i, j) !=
3257           el_act_dir2img(element, ACTION_DEFAULT, j))
3258         found = TRUE;
3259
3260     if (found)
3261       num_special_action++;
3262     else
3263       break;
3264   }
3265
3266   return num_special_action;
3267 }
3268
3269
3270 /*
3271   =============================================================================
3272   InitGame()
3273   -----------------------------------------------------------------------------
3274   initialize and start new game
3275   =============================================================================
3276 */
3277
3278 #if DEBUG_INIT_PLAYER
3279 static void DebugPrintPlayerStatus(char *message)
3280 {
3281   int i;
3282
3283   if (!options.debug)
3284     return;
3285
3286   printf("%s:\n", message);
3287
3288   for (i = 0; i < MAX_PLAYERS; i++)
3289   {
3290     struct PlayerInfo *player = &stored_player[i];
3291
3292     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3293            i + 1,
3294            player->present,
3295            player->connected,
3296            player->connected_locally,
3297            player->connected_network,
3298            player->active);
3299
3300     if (local_player == player)
3301       printf(" (local player)");
3302
3303     printf("\n");
3304   }
3305 }
3306 #endif
3307
3308 void InitGame()
3309 {
3310   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3311   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3312   int fade_mask = REDRAW_FIELD;
3313
3314   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3315   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3316   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3317   int initial_move_dir = MV_DOWN;
3318   int i, j, x, y;
3319
3320   // required here to update video display before fading (FIX THIS)
3321   DrawMaskedBorder(REDRAW_DOOR_2);
3322
3323   if (!game.restart_level)
3324     CloseDoor(DOOR_CLOSE_1);
3325
3326   SetGameStatus(GAME_MODE_PLAYING);
3327
3328   if (level_editor_test_game)
3329     FadeSkipNextFadeIn();
3330   else
3331     FadeSetEnterScreen();
3332
3333   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3334     fade_mask = REDRAW_ALL;
3335
3336   FadeLevelSoundsAndMusic();
3337
3338   ExpireSoundLoops(TRUE);
3339
3340   FadeOut(fade_mask);
3341
3342   /* needed if different viewport properties defined for playing */
3343   ChangeViewportPropertiesIfNeeded();
3344
3345   ClearField();
3346
3347   DrawCompleteVideoDisplay();
3348
3349   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3350
3351   InitGameEngine();
3352   InitGameControlValues();
3353
3354   /* don't play tapes over network */
3355   network_playing = (network.enabled && !tape.playing);
3356
3357   for (i = 0; i < MAX_PLAYERS; i++)
3358   {
3359     struct PlayerInfo *player = &stored_player[i];
3360
3361     player->index_nr = i;
3362     player->index_bit = (1 << i);
3363     player->element_nr = EL_PLAYER_1 + i;
3364
3365     player->present = FALSE;
3366     player->active = FALSE;
3367     player->mapped = FALSE;
3368
3369     player->killed = FALSE;
3370     player->reanimated = FALSE;
3371
3372     player->action = 0;
3373     player->effective_action = 0;
3374     player->programmed_action = 0;
3375
3376     player->mouse_action.lx = 0;
3377     player->mouse_action.ly = 0;
3378     player->mouse_action.button = 0;
3379     player->mouse_action.button_hint = 0;
3380
3381     player->effective_mouse_action.lx = 0;
3382     player->effective_mouse_action.ly = 0;
3383     player->effective_mouse_action.button = 0;
3384     player->effective_mouse_action.button_hint = 0;
3385
3386     player->score = 0;
3387     player->score_final = 0;
3388
3389     player->health = MAX_HEALTH;
3390     player->health_final = MAX_HEALTH;
3391
3392     player->gems_still_needed = level.gems_needed;
3393     player->sokobanfields_still_needed = 0;
3394     player->lights_still_needed = 0;
3395     player->players_still_needed = 0;
3396     player->friends_still_needed = 0;
3397
3398     for (j = 0; j < MAX_NUM_KEYS; j++)
3399       player->key[j] = FALSE;
3400
3401     player->num_white_keys = 0;
3402
3403     player->dynabomb_count = 0;
3404     player->dynabomb_size = 1;
3405     player->dynabombs_left = 0;
3406     player->dynabomb_xl = FALSE;
3407
3408     player->MovDir = initial_move_dir;
3409     player->MovPos = 0;
3410     player->GfxPos = 0;
3411     player->GfxDir = initial_move_dir;
3412     player->GfxAction = ACTION_DEFAULT;
3413     player->Frame = 0;
3414     player->StepFrame = 0;
3415
3416     player->initial_element = player->element_nr;
3417     player->artwork_element =
3418       (level.use_artwork_element[i] ? level.artwork_element[i] :
3419        player->element_nr);
3420     player->use_murphy = FALSE;
3421
3422     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3423     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3424
3425     player->gravity = level.initial_player_gravity[i];
3426
3427     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3428
3429     player->actual_frame_counter = 0;
3430
3431     player->step_counter = 0;
3432
3433     player->last_move_dir = initial_move_dir;
3434
3435     player->is_active = FALSE;
3436
3437     player->is_waiting = FALSE;
3438     player->is_moving = FALSE;
3439     player->is_auto_moving = FALSE;
3440     player->is_digging = FALSE;
3441     player->is_snapping = FALSE;
3442     player->is_collecting = FALSE;
3443     player->is_pushing = FALSE;
3444     player->is_switching = FALSE;
3445     player->is_dropping = FALSE;
3446     player->is_dropping_pressed = FALSE;
3447
3448     player->is_bored = FALSE;
3449     player->is_sleeping = FALSE;
3450
3451     player->was_waiting = TRUE;
3452     player->was_moving = FALSE;
3453     player->was_snapping = FALSE;
3454     player->was_dropping = FALSE;
3455
3456     player->force_dropping = FALSE;
3457
3458     player->frame_counter_bored = -1;
3459     player->frame_counter_sleeping = -1;
3460
3461     player->anim_delay_counter = 0;
3462     player->post_delay_counter = 0;
3463
3464     player->dir_waiting = initial_move_dir;
3465     player->action_waiting = ACTION_DEFAULT;
3466     player->last_action_waiting = ACTION_DEFAULT;
3467     player->special_action_bored = ACTION_DEFAULT;
3468     player->special_action_sleeping = ACTION_DEFAULT;
3469
3470     player->switch_x = -1;
3471     player->switch_y = -1;
3472
3473     player->drop_x = -1;
3474     player->drop_y = -1;
3475
3476     player->show_envelope = 0;
3477
3478     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3479
3480     player->push_delay       = -1;      /* initialized when pushing starts */
3481     player->push_delay_value = game.initial_push_delay_value;
3482
3483     player->drop_delay = 0;
3484     player->drop_pressed_delay = 0;
3485
3486     player->last_jx = -1;
3487     player->last_jy = -1;
3488     player->jx = -1;
3489     player->jy = -1;
3490
3491     player->shield_normal_time_left = 0;
3492     player->shield_deadly_time_left = 0;
3493
3494     player->inventory_infinite_element = EL_UNDEFINED;
3495     player->inventory_size = 0;
3496
3497     if (level.use_initial_inventory[i])
3498     {
3499       for (j = 0; j < level.initial_inventory_size[i]; j++)
3500       {
3501         int element = level.initial_inventory_content[i][j];
3502         int collect_count = element_info[element].collect_count_initial;
3503         int k;
3504
3505         if (!IS_CUSTOM_ELEMENT(element))
3506           collect_count = 1;
3507
3508         if (collect_count == 0)
3509           player->inventory_infinite_element = element;
3510         else
3511           for (k = 0; k < collect_count; k++)
3512             if (player->inventory_size < MAX_INVENTORY_SIZE)
3513               player->inventory_element[player->inventory_size++] = element;
3514       }
3515     }
3516
3517     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3518     SnapField(player, 0, 0);
3519
3520     player->LevelSolved = FALSE;
3521     player->GameOver = FALSE;
3522
3523     player->LevelSolved_GameWon = FALSE;
3524     player->LevelSolved_GameEnd = FALSE;
3525     player->LevelSolved_PanelOff = FALSE;
3526     player->LevelSolved_SaveTape = FALSE;
3527     player->LevelSolved_SaveScore = FALSE;
3528
3529     player->LevelSolved_CountingTime = 0;
3530     player->LevelSolved_CountingScore = 0;
3531     player->LevelSolved_CountingHealth = 0;
3532
3533     map_player_action[i] = i;
3534   }
3535
3536   network_player_action_received = FALSE;
3537
3538   /* initial null action */
3539   if (network_playing)
3540     SendToServer_MovePlayer(MV_NONE);
3541
3542   ZX = ZY = -1;
3543   ExitX = ExitY = -1;
3544
3545   FrameCounter = 0;
3546   TimeFrames = 0;
3547   TimePlayed = 0;
3548   TimeLeft = level.time;
3549   TapeTime = 0;
3550
3551   ScreenMovDir = MV_NONE;
3552   ScreenMovPos = 0;
3553   ScreenGfxPos = 0;
3554
3555   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3556
3557   AllPlayersGone = FALSE;
3558
3559   game.no_time_limit = (level.time == 0);
3560
3561   game.yamyam_content_nr = 0;
3562   game.robot_wheel_active = FALSE;
3563   game.magic_wall_active = FALSE;
3564   game.magic_wall_time_left = 0;
3565   game.light_time_left = 0;
3566   game.timegate_time_left = 0;
3567   game.switchgate_pos = 0;
3568   game.wind_direction = level.wind_direction_initial;
3569
3570   game.lenses_time_left = 0;
3571   game.magnify_time_left = 0;
3572
3573   game.ball_state = level.ball_state_initial;
3574   game.ball_content_nr = 0;
3575
3576   game.envelope_active = FALSE;
3577
3578   for (i = 0; i < NUM_BELTS; i++)
3579   {
3580     game.belt_dir[i] = MV_NONE;
3581     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3582   }
3583
3584   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3585     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3586
3587 #if DEBUG_INIT_PLAYER
3588   DebugPrintPlayerStatus("Player status at level initialization");
3589 #endif
3590
3591   SCAN_PLAYFIELD(x, y)
3592   {
3593     Feld[x][y] = level.field[x][y];
3594     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3595     ChangeDelay[x][y] = 0;
3596     ChangePage[x][y] = -1;
3597     CustomValue[x][y] = 0;              /* initialized in InitField() */
3598     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3599     AmoebaNr[x][y] = 0;
3600     WasJustMoving[x][y] = 0;
3601     WasJustFalling[x][y] = 0;
3602     CheckCollision[x][y] = 0;
3603     CheckImpact[x][y] = 0;
3604     Stop[x][y] = FALSE;
3605     Pushed[x][y] = FALSE;
3606
3607     ChangeCount[x][y] = 0;
3608     ChangeEvent[x][y] = -1;
3609
3610     ExplodePhase[x][y] = 0;
3611     ExplodeDelay[x][y] = 0;
3612     ExplodeField[x][y] = EX_TYPE_NONE;
3613
3614     RunnerVisit[x][y] = 0;
3615     PlayerVisit[x][y] = 0;
3616
3617     GfxFrame[x][y] = 0;
3618     GfxRandom[x][y] = INIT_GFX_RANDOM();
3619     GfxElement[x][y] = EL_UNDEFINED;
3620     GfxAction[x][y] = ACTION_DEFAULT;
3621     GfxDir[x][y] = MV_NONE;
3622     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3623   }
3624
3625   SCAN_PLAYFIELD(x, y)
3626   {
3627     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3628       emulate_bd = FALSE;
3629     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3630       emulate_sb = FALSE;
3631     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3632       emulate_sp = FALSE;
3633
3634     InitField(x, y, TRUE);
3635
3636     ResetGfxAnimation(x, y);
3637   }
3638
3639   InitBeltMovement();
3640
3641   for (i = 0; i < MAX_PLAYERS; i++)
3642   {
3643     struct PlayerInfo *player = &stored_player[i];
3644
3645     /* set number of special actions for bored and sleeping animation */
3646     player->num_special_action_bored =
3647       get_num_special_action(player->artwork_element,
3648                              ACTION_BORING_1, ACTION_BORING_LAST);
3649     player->num_special_action_sleeping =
3650       get_num_special_action(player->artwork_element,
3651                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3652   }
3653
3654   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3655                     emulate_sb ? EMU_SOKOBAN :
3656                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3657
3658   /* initialize type of slippery elements */
3659   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3660   {
3661     if (!IS_CUSTOM_ELEMENT(i))
3662     {
3663       /* default: elements slip down either to the left or right randomly */
3664       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3665
3666       /* SP style elements prefer to slip down on the left side */
3667       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3668         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3669
3670       /* BD style elements prefer to slip down on the left side */
3671       if (game.emulation == EMU_BOULDERDASH)
3672         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3673     }
3674   }
3675
3676   /* initialize explosion and ignition delay */
3677   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3678   {
3679     if (!IS_CUSTOM_ELEMENT(i))
3680     {
3681       int num_phase = 8;
3682       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3683                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3684                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3685       int last_phase = (num_phase + 1) * delay;
3686       int half_phase = (num_phase / 2) * delay;
3687
3688       element_info[i].explosion_delay = last_phase - 1;
3689       element_info[i].ignition_delay = half_phase;
3690
3691       if (i == EL_BLACK_ORB)
3692         element_info[i].ignition_delay = 1;
3693     }
3694   }
3695
3696   /* correct non-moving belts to start moving left */
3697   for (i = 0; i < NUM_BELTS; i++)
3698     if (game.belt_dir[i] == MV_NONE)
3699       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3700
3701 #if USE_NEW_PLAYER_ASSIGNMENTS
3702   for (i = 0; i < MAX_PLAYERS; i++)
3703   {
3704     stored_player[i].connected = FALSE;
3705
3706     /* in network game mode, the local player might not be the first player */
3707     if (stored_player[i].connected_locally)
3708       local_player = &stored_player[i];
3709   }
3710
3711   if (!network.enabled)
3712     local_player->connected = TRUE;
3713
3714   if (tape.playing)
3715   {
3716     for (i = 0; i < MAX_PLAYERS; i++)
3717       stored_player[i].connected = tape.player_participates[i];
3718   }
3719   else if (network.enabled)
3720   {
3721     /* add team mode players connected over the network (needed for correct
3722        assignment of player figures from level to locally playing players) */
3723
3724     for (i = 0; i < MAX_PLAYERS; i++)
3725       if (stored_player[i].connected_network)
3726         stored_player[i].connected = TRUE;
3727   }
3728   else if (game.team_mode)
3729   {
3730     /* try to guess locally connected team mode players (needed for correct
3731        assignment of player figures from level to locally playing players) */
3732
3733     for (i = 0; i < MAX_PLAYERS; i++)
3734       if (setup.input[i].use_joystick ||
3735           setup.input[i].key.left != KSYM_UNDEFINED)
3736         stored_player[i].connected = TRUE;
3737   }
3738
3739 #if DEBUG_INIT_PLAYER
3740   DebugPrintPlayerStatus("Player status after level initialization");
3741 #endif
3742
3743 #if DEBUG_INIT_PLAYER
3744   if (options.debug)
3745     printf("Reassigning players ...\n");
3746 #endif
3747
3748   /* check if any connected player was not found in playfield */
3749   for (i = 0; i < MAX_PLAYERS; i++)
3750   {
3751     struct PlayerInfo *player = &stored_player[i];
3752
3753     if (player->connected && !player->present)
3754     {
3755       struct PlayerInfo *field_player = NULL;
3756
3757 #if DEBUG_INIT_PLAYER
3758       if (options.debug)
3759         printf("- looking for field player for player %d ...\n", i + 1);
3760 #endif
3761
3762       /* assign first free player found that is present in the playfield */
3763
3764       /* first try: look for unmapped playfield player that is not connected */
3765       for (j = 0; j < MAX_PLAYERS; j++)
3766         if (field_player == NULL &&
3767             stored_player[j].present &&
3768             !stored_player[j].mapped &&
3769             !stored_player[j].connected)
3770           field_player = &stored_player[j];
3771
3772       /* second try: look for *any* unmapped playfield player */
3773       for (j = 0; j < MAX_PLAYERS; j++)
3774         if (field_player == NULL &&
3775             stored_player[j].present &&
3776             !stored_player[j].mapped)
3777           field_player = &stored_player[j];
3778
3779       if (field_player != NULL)
3780       {
3781         int jx = field_player->jx, jy = field_player->jy;
3782
3783 #if DEBUG_INIT_PLAYER
3784         if (options.debug)
3785           printf("- found player %d\n", field_player->index_nr + 1);
3786 #endif
3787
3788         player->present = FALSE;
3789         player->active = FALSE;
3790
3791         field_player->present = TRUE;
3792         field_player->active = TRUE;
3793
3794         /*
3795         player->initial_element = field_player->initial_element;
3796         player->artwork_element = field_player->artwork_element;
3797
3798         player->block_last_field       = field_player->block_last_field;
3799         player->block_delay_adjustment = field_player->block_delay_adjustment;
3800         */
3801
3802         StorePlayer[jx][jy] = field_player->element_nr;
3803
3804         field_player->jx = field_player->last_jx = jx;
3805         field_player->jy = field_player->last_jy = jy;
3806
3807         if (local_player == player)
3808           local_player = field_player;
3809
3810         map_player_action[field_player->index_nr] = i;
3811
3812         field_player->mapped = TRUE;
3813
3814 #if DEBUG_INIT_PLAYER
3815         if (options.debug)
3816           printf("- map_player_action[%d] == %d\n",
3817                  field_player->index_nr + 1, i + 1);
3818 #endif
3819       }
3820     }
3821
3822     if (player->connected && player->present)
3823       player->mapped = TRUE;
3824   }
3825
3826 #if DEBUG_INIT_PLAYER
3827   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3828 #endif
3829
3830 #else
3831
3832   /* check if any connected player was not found in playfield */
3833   for (i = 0; i < MAX_PLAYERS; i++)
3834   {
3835     struct PlayerInfo *player = &stored_player[i];
3836
3837     if (player->connected && !player->present)
3838     {
3839       for (j = 0; j < MAX_PLAYERS; j++)
3840       {
3841         struct PlayerInfo *field_player = &stored_player[j];
3842         int jx = field_player->jx, jy = field_player->jy;
3843
3844         /* assign first free player found that is present in the playfield */
3845         if (field_player->present && !field_player->connected)
3846         {
3847           player->present = TRUE;
3848           player->active = TRUE;
3849
3850           field_player->present = FALSE;
3851           field_player->active = FALSE;
3852
3853           player->initial_element = field_player->initial_element;
3854           player->artwork_element = field_player->artwork_element;
3855
3856           player->block_last_field       = field_player->block_last_field;
3857           player->block_delay_adjustment = field_player->block_delay_adjustment;
3858
3859           StorePlayer[jx][jy] = player->element_nr;
3860
3861           player->jx = player->last_jx = jx;
3862           player->jy = player->last_jy = jy;
3863
3864           break;
3865         }
3866       }
3867     }
3868   }
3869 #endif
3870
3871 #if 0
3872   printf("::: local_player->present == %d\n", local_player->present);
3873 #endif
3874
3875   /* set focus to local player for network games, else to all players */
3876   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3877   game.centered_player_nr_next = game.centered_player_nr;
3878   game.set_centered_player = FALSE;
3879
3880   if (network_playing && tape.recording)
3881   {
3882     /* store client dependent player focus when recording network games */
3883     tape.centered_player_nr_next = game.centered_player_nr_next;
3884     tape.set_centered_player = TRUE;
3885   }
3886
3887   if (tape.playing)
3888   {
3889     /* when playing a tape, eliminate all players who do not participate */
3890
3891 #if USE_NEW_PLAYER_ASSIGNMENTS
3892
3893     if (!game.team_mode)
3894     {
3895       for (i = 0; i < MAX_PLAYERS; i++)
3896       {
3897         if (stored_player[i].active &&
3898             !tape.player_participates[map_player_action[i]])
3899         {
3900           struct PlayerInfo *player = &stored_player[i];
3901           int jx = player->jx, jy = player->jy;
3902
3903 #if DEBUG_INIT_PLAYER
3904           if (options.debug)
3905             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3906 #endif
3907
3908           player->active = FALSE;
3909           StorePlayer[jx][jy] = 0;
3910           Feld[jx][jy] = EL_EMPTY;
3911         }
3912       }
3913     }
3914
3915 #else
3916
3917     for (i = 0; i < MAX_PLAYERS; i++)
3918     {
3919       if (stored_player[i].active &&
3920           !tape.player_participates[i])
3921       {
3922         struct PlayerInfo *player = &stored_player[i];
3923         int jx = player->jx, jy = player->jy;
3924
3925         player->active = FALSE;
3926         StorePlayer[jx][jy] = 0;
3927         Feld[jx][jy] = EL_EMPTY;
3928       }
3929     }
3930 #endif
3931   }
3932   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3933   {
3934     /* when in single player mode, eliminate all but the local player */
3935
3936     for (i = 0; i < MAX_PLAYERS; i++)
3937     {
3938       struct PlayerInfo *player = &stored_player[i];
3939
3940       if (player->active && player != local_player)
3941       {
3942         int jx = player->jx, jy = player->jy;
3943
3944         player->active = FALSE;
3945         player->present = FALSE;
3946
3947         StorePlayer[jx][jy] = 0;
3948         Feld[jx][jy] = EL_EMPTY;
3949       }
3950     }
3951   }
3952
3953   for (i = 0; i < MAX_PLAYERS; i++)
3954     if (stored_player[i].active)
3955       local_player->players_still_needed++;
3956
3957   /* when recording the game, store which players take part in the game */
3958   if (tape.recording)
3959   {
3960 #if USE_NEW_PLAYER_ASSIGNMENTS
3961     for (i = 0; i < MAX_PLAYERS; i++)
3962       if (stored_player[i].connected)
3963         tape.player_participates[i] = TRUE;
3964 #else
3965     for (i = 0; i < MAX_PLAYERS; i++)
3966       if (stored_player[i].active)
3967         tape.player_participates[i] = TRUE;
3968 #endif
3969   }
3970
3971 #if DEBUG_INIT_PLAYER
3972   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3973 #endif
3974
3975   if (BorderElement == EL_EMPTY)
3976   {
3977     SBX_Left = 0;
3978     SBX_Right = lev_fieldx - SCR_FIELDX;
3979     SBY_Upper = 0;
3980     SBY_Lower = lev_fieldy - SCR_FIELDY;
3981   }
3982   else
3983   {
3984     SBX_Left = -1;
3985     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3986     SBY_Upper = -1;
3987     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3988   }
3989
3990   if (full_lev_fieldx <= SCR_FIELDX)
3991     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3992   if (full_lev_fieldy <= SCR_FIELDY)
3993     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3994
3995   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3996     SBX_Left--;
3997   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3998     SBY_Upper--;
3999
4000   /* if local player not found, look for custom element that might create
4001      the player (make some assumptions about the right custom element) */
4002   if (!local_player->present)
4003   {
4004     int start_x = 0, start_y = 0;
4005     int found_rating = 0;
4006     int found_element = EL_UNDEFINED;
4007     int player_nr = local_player->index_nr;
4008
4009     SCAN_PLAYFIELD(x, y)
4010     {
4011       int element = Feld[x][y];
4012       int content;
4013       int xx, yy;
4014       boolean is_player;
4015
4016       if (level.use_start_element[player_nr] &&
4017           level.start_element[player_nr] == element &&
4018           found_rating < 4)
4019       {
4020         start_x = x;
4021         start_y = y;
4022
4023         found_rating = 4;
4024         found_element = element;
4025       }
4026
4027       if (!IS_CUSTOM_ELEMENT(element))
4028         continue;
4029
4030       if (CAN_CHANGE(element))
4031       {
4032         for (i = 0; i < element_info[element].num_change_pages; i++)
4033         {
4034           /* check for player created from custom element as single target */
4035           content = element_info[element].change_page[i].target_element;
4036           is_player = ELEM_IS_PLAYER(content);
4037
4038           if (is_player && (found_rating < 3 ||
4039                             (found_rating == 3 && element < found_element)))
4040           {
4041             start_x = x;
4042             start_y = y;
4043
4044             found_rating = 3;
4045             found_element = element;
4046           }
4047         }
4048       }
4049
4050       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4051       {
4052         /* check for player created from custom element as explosion content */
4053         content = element_info[element].content.e[xx][yy];
4054         is_player = ELEM_IS_PLAYER(content);
4055
4056         if (is_player && (found_rating < 2 ||
4057                           (found_rating == 2 && element < found_element)))
4058         {
4059           start_x = x + xx - 1;
4060           start_y = y + yy - 1;
4061
4062           found_rating = 2;
4063           found_element = element;
4064         }
4065
4066         if (!CAN_CHANGE(element))
4067           continue;
4068
4069         for (i = 0; i < element_info[element].num_change_pages; i++)
4070         {
4071           /* check for player created from custom element as extended target */
4072           content =
4073             element_info[element].change_page[i].target_content.e[xx][yy];
4074
4075           is_player = ELEM_IS_PLAYER(content);
4076
4077           if (is_player && (found_rating < 1 ||
4078                             (found_rating == 1 && element < found_element)))
4079           {
4080             start_x = x + xx - 1;
4081             start_y = y + yy - 1;
4082
4083             found_rating = 1;
4084             found_element = element;
4085           }
4086         }
4087       }
4088     }
4089
4090     scroll_x = SCROLL_POSITION_X(start_x);
4091     scroll_y = SCROLL_POSITION_Y(start_y);
4092   }
4093   else
4094   {
4095     scroll_x = SCROLL_POSITION_X(local_player->jx);
4096     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4097   }
4098
4099   /* !!! FIX THIS (START) !!! */
4100   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4101   {
4102     InitGameEngine_EM();
4103   }
4104   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4105   {
4106     InitGameEngine_SP();
4107   }
4108   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4109   {
4110     InitGameEngine_MM();
4111   }
4112   else
4113   {
4114     DrawLevel(REDRAW_FIELD);
4115     DrawAllPlayers();
4116
4117     /* after drawing the level, correct some elements */
4118     if (game.timegate_time_left == 0)
4119       CloseAllOpenTimegates();
4120   }
4121
4122   /* blit playfield from scroll buffer to normal back buffer for fading in */
4123   BlitScreenToBitmap(backbuffer);
4124   /* !!! FIX THIS (END) !!! */
4125
4126   DrawMaskedBorder(fade_mask);
4127
4128   FadeIn(fade_mask);
4129
4130 #if 1
4131   // full screen redraw is required at this point in the following cases:
4132   // - special editor door undrawn when game was started from level editor
4133   // - drawing area (playfield) was changed and has to be removed completely
4134   redraw_mask = REDRAW_ALL;
4135   BackToFront();
4136 #endif
4137
4138   if (!game.restart_level)
4139   {
4140     /* copy default game door content to main double buffer */
4141
4142     /* !!! CHECK AGAIN !!! */
4143     SetPanelBackground();
4144     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4145     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4146   }
4147
4148   SetPanelBackground();
4149   SetDrawBackgroundMask(REDRAW_DOOR_1);
4150
4151   UpdateAndDisplayGameControlValues();
4152
4153   if (!game.restart_level)
4154   {
4155     UnmapGameButtons();
4156     UnmapTapeButtons();
4157
4158     FreeGameButtons();
4159     CreateGameButtons();
4160
4161     MapGameButtons();
4162     MapTapeButtons();
4163
4164     /* copy actual game door content to door double buffer for OpenDoor() */
4165     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4166
4167     OpenDoor(DOOR_OPEN_ALL);
4168
4169     KeyboardAutoRepeatOffUnlessAutoplay();
4170
4171 #if DEBUG_INIT_PLAYER
4172     DebugPrintPlayerStatus("Player status (final)");
4173 #endif
4174   }
4175
4176   UnmapAllGadgets();
4177
4178   MapGameButtons();
4179   MapTapeButtons();
4180
4181   if (!game.restart_level && !tape.playing)
4182   {
4183     LevelStats_incPlayed(level_nr);
4184
4185     SaveLevelSetup_SeriesInfo();
4186   }
4187
4188   game.restart_level = FALSE;
4189   game.restart_game_message = NULL;
4190
4191   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4192     InitGameActions_MM();
4193
4194   SaveEngineSnapshotToListInitial();
4195
4196   if (!game.restart_level)
4197   {
4198     PlaySound(SND_GAME_STARTING);
4199
4200     if (setup.sound_music)
4201       PlayLevelMusic();
4202   }
4203 }
4204
4205 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4206                         int actual_player_x, int actual_player_y)
4207 {
4208   /* this is used for non-R'n'D game engines to update certain engine values */
4209
4210   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4211   {
4212     actual_player_x = correctLevelPosX_EM(actual_player_x);
4213     actual_player_y = correctLevelPosY_EM(actual_player_y);
4214   }
4215
4216   /* needed to determine if sounds are played within the visible screen area */
4217   scroll_x = actual_scroll_x;
4218   scroll_y = actual_scroll_y;
4219
4220   /* needed to get player position for "follow finger" playing input method */
4221   local_player->jx = actual_player_x;
4222   local_player->jy = actual_player_y;
4223 }
4224
4225 void InitMovDir(int x, int y)
4226 {
4227   int i, element = Feld[x][y];
4228   static int xy[4][2] =
4229   {
4230     {  0, +1 },
4231     { +1,  0 },
4232     {  0, -1 },
4233     { -1,  0 }
4234   };
4235   static int direction[3][4] =
4236   {
4237     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4238     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4239     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4240   };
4241
4242   switch (element)
4243   {
4244     case EL_BUG_RIGHT:
4245     case EL_BUG_UP:
4246     case EL_BUG_LEFT:
4247     case EL_BUG_DOWN:
4248       Feld[x][y] = EL_BUG;
4249       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4250       break;
4251
4252     case EL_SPACESHIP_RIGHT:
4253     case EL_SPACESHIP_UP:
4254     case EL_SPACESHIP_LEFT:
4255     case EL_SPACESHIP_DOWN:
4256       Feld[x][y] = EL_SPACESHIP;
4257       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4258       break;
4259
4260     case EL_BD_BUTTERFLY_RIGHT:
4261     case EL_BD_BUTTERFLY_UP:
4262     case EL_BD_BUTTERFLY_LEFT:
4263     case EL_BD_BUTTERFLY_DOWN:
4264       Feld[x][y] = EL_BD_BUTTERFLY;
4265       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4266       break;
4267
4268     case EL_BD_FIREFLY_RIGHT:
4269     case EL_BD_FIREFLY_UP:
4270     case EL_BD_FIREFLY_LEFT:
4271     case EL_BD_FIREFLY_DOWN:
4272       Feld[x][y] = EL_BD_FIREFLY;
4273       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4274       break;
4275
4276     case EL_PACMAN_RIGHT:
4277     case EL_PACMAN_UP:
4278     case EL_PACMAN_LEFT:
4279     case EL_PACMAN_DOWN:
4280       Feld[x][y] = EL_PACMAN;
4281       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4282       break;
4283
4284     case EL_YAMYAM_LEFT:
4285     case EL_YAMYAM_RIGHT:
4286     case EL_YAMYAM_UP:
4287     case EL_YAMYAM_DOWN:
4288       Feld[x][y] = EL_YAMYAM;
4289       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4290       break;
4291
4292     case EL_SP_SNIKSNAK:
4293       MovDir[x][y] = MV_UP;
4294       break;
4295
4296     case EL_SP_ELECTRON:
4297       MovDir[x][y] = MV_LEFT;
4298       break;
4299
4300     case EL_MOLE_LEFT:
4301     case EL_MOLE_RIGHT:
4302     case EL_MOLE_UP:
4303     case EL_MOLE_DOWN:
4304       Feld[x][y] = EL_MOLE;
4305       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4306       break;
4307
4308     default:
4309       if (IS_CUSTOM_ELEMENT(element))
4310       {
4311         struct ElementInfo *ei = &element_info[element];
4312         int move_direction_initial = ei->move_direction_initial;
4313         int move_pattern = ei->move_pattern;
4314
4315         if (move_direction_initial == MV_START_PREVIOUS)
4316         {
4317           if (MovDir[x][y] != MV_NONE)
4318             return;
4319
4320           move_direction_initial = MV_START_AUTOMATIC;
4321         }
4322
4323         if (move_direction_initial == MV_START_RANDOM)
4324           MovDir[x][y] = 1 << RND(4);
4325         else if (move_direction_initial & MV_ANY_DIRECTION)
4326           MovDir[x][y] = move_direction_initial;
4327         else if (move_pattern == MV_ALL_DIRECTIONS ||
4328                  move_pattern == MV_TURNING_LEFT ||
4329                  move_pattern == MV_TURNING_RIGHT ||
4330                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4331                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4332                  move_pattern == MV_TURNING_RANDOM)
4333           MovDir[x][y] = 1 << RND(4);
4334         else if (move_pattern == MV_HORIZONTAL)
4335           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4336         else if (move_pattern == MV_VERTICAL)
4337           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4338         else if (move_pattern & MV_ANY_DIRECTION)
4339           MovDir[x][y] = element_info[element].move_pattern;
4340         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4341                  move_pattern == MV_ALONG_RIGHT_SIDE)
4342         {
4343           /* use random direction as default start direction */
4344           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4345             MovDir[x][y] = 1 << RND(4);
4346
4347           for (i = 0; i < NUM_DIRECTIONS; i++)
4348           {
4349             int x1 = x + xy[i][0];
4350             int y1 = y + xy[i][1];
4351
4352             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4353             {
4354               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4355                 MovDir[x][y] = direction[0][i];
4356               else
4357                 MovDir[x][y] = direction[1][i];
4358
4359               break;
4360             }
4361           }
4362         }                
4363       }
4364       else
4365       {
4366         MovDir[x][y] = 1 << RND(4);
4367
4368         if (element != EL_BUG &&
4369             element != EL_SPACESHIP &&
4370             element != EL_BD_BUTTERFLY &&
4371             element != EL_BD_FIREFLY)
4372           break;
4373
4374         for (i = 0; i < NUM_DIRECTIONS; i++)
4375         {
4376           int x1 = x + xy[i][0];
4377           int y1 = y + xy[i][1];
4378
4379           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4380           {
4381             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4382             {
4383               MovDir[x][y] = direction[0][i];
4384               break;
4385             }
4386             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4387                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4388             {
4389               MovDir[x][y] = direction[1][i];
4390               break;
4391             }
4392           }
4393         }
4394       }
4395       break;
4396   }
4397
4398   GfxDir[x][y] = MovDir[x][y];
4399 }
4400
4401 void InitAmoebaNr(int x, int y)
4402 {
4403   int i;
4404   int group_nr = AmoebeNachbarNr(x, y);
4405
4406   if (group_nr == 0)
4407   {
4408     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4409     {
4410       if (AmoebaCnt[i] == 0)
4411       {
4412         group_nr = i;
4413         break;
4414       }
4415     }
4416   }
4417
4418   AmoebaNr[x][y] = group_nr;
4419   AmoebaCnt[group_nr]++;
4420   AmoebaCnt2[group_nr]++;
4421 }
4422
4423 static void PlayerWins(struct PlayerInfo *player)
4424 {
4425   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4426       local_player->players_still_needed > 0)
4427     return;
4428
4429   player->LevelSolved = TRUE;
4430   player->GameOver = TRUE;
4431
4432   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4433                          level.native_em_level->lev->score :
4434                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4435                          game_mm.score :
4436                          player->score);
4437   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4438                           MM_HEALTH(game_mm.laser_overload_value) :
4439                           player->health);
4440
4441   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4442                                       TimeLeft);
4443   player->LevelSolved_CountingScore = player->score_final;
4444   player->LevelSolved_CountingHealth = player->health_final;
4445 }
4446
4447 void GameWon()
4448 {
4449   static int time_count_steps;
4450   static int time, time_final;
4451   static int score, score_final;
4452   static int health, health_final;
4453   static int game_over_delay_1 = 0;
4454   static int game_over_delay_2 = 0;
4455   static int game_over_delay_3 = 0;
4456   int game_over_delay_value_1 = 50;
4457   int game_over_delay_value_2 = 25;
4458   int game_over_delay_value_3 = 50;
4459
4460   if (!local_player->LevelSolved_GameWon)
4461   {
4462     int i;
4463
4464     /* do not start end game actions before the player stops moving (to exit) */
4465     if (local_player->MovPos)
4466       return;
4467
4468     local_player->LevelSolved_GameWon = TRUE;
4469     local_player->LevelSolved_SaveTape = tape.recording;
4470     local_player->LevelSolved_SaveScore = !tape.playing;
4471
4472     if (!tape.playing)
4473     {
4474       LevelStats_incSolved(level_nr);
4475
4476       SaveLevelSetup_SeriesInfo();
4477     }
4478
4479     if (tape.auto_play)         /* tape might already be stopped here */
4480       tape.auto_play_level_solved = TRUE;
4481
4482     TapeStop();
4483
4484     game_over_delay_1 = 0;
4485     game_over_delay_2 = 0;
4486     game_over_delay_3 = game_over_delay_value_3;
4487
4488     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4489     score = score_final = local_player->score_final;
4490     health = health_final = local_player->health_final;
4491
4492     if (level.score[SC_TIME_BONUS] > 0)
4493     {
4494       if (TimeLeft > 0)
4495       {
4496         time_final = 0;
4497         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4498       }
4499       else if (game.no_time_limit && TimePlayed < 999)
4500       {
4501         time_final = 999;
4502         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4503       }
4504
4505       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4506
4507       game_over_delay_1 = game_over_delay_value_1;
4508
4509       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4510       {
4511         health_final = 0;
4512         score_final += health * level.score[SC_TIME_BONUS];
4513
4514         game_over_delay_2 = game_over_delay_value_2;
4515       }
4516
4517       local_player->score_final = score_final;
4518       local_player->health_final = health_final;
4519     }
4520
4521     if (level_editor_test_game)
4522     {
4523       time = time_final;
4524       score = score_final;
4525
4526       local_player->LevelSolved_CountingTime = time;
4527       local_player->LevelSolved_CountingScore = score;
4528
4529       game_panel_controls[GAME_PANEL_TIME].value = time;
4530       game_panel_controls[GAME_PANEL_SCORE].value = score;
4531
4532       DisplayGameControlValues();
4533     }
4534
4535     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4536     {
4537       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4538       {
4539         /* close exit door after last player */
4540         if ((AllPlayersGone &&
4541              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4542               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4543               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4544             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4545             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4546         {
4547           int element = Feld[ExitX][ExitY];
4548
4549           Feld[ExitX][ExitY] =
4550             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4551              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4552              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4553              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4554              EL_EM_STEEL_EXIT_CLOSING);
4555
4556           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4557         }
4558
4559         /* player disappears */
4560         DrawLevelField(ExitX, ExitY);
4561       }
4562
4563       for (i = 0; i < MAX_PLAYERS; i++)
4564       {
4565         struct PlayerInfo *player = &stored_player[i];
4566
4567         if (player->present)
4568         {
4569           RemovePlayer(player);
4570
4571           /* player disappears */
4572           DrawLevelField(player->jx, player->jy);
4573         }
4574       }
4575     }
4576
4577     PlaySound(SND_GAME_WINNING);
4578   }
4579
4580   if (game_over_delay_1 > 0)
4581   {
4582     game_over_delay_1--;
4583
4584     return;
4585   }
4586
4587   if (time != time_final)
4588   {
4589     int time_to_go = ABS(time_final - time);
4590     int time_count_dir = (time < time_final ? +1 : -1);
4591
4592     if (time_to_go < time_count_steps)
4593       time_count_steps = 1;
4594
4595     time  += time_count_steps * time_count_dir;
4596     score += time_count_steps * level.score[SC_TIME_BONUS];
4597
4598     local_player->LevelSolved_CountingTime = time;
4599     local_player->LevelSolved_CountingScore = score;
4600
4601     game_panel_controls[GAME_PANEL_TIME].value = time;
4602     game_panel_controls[GAME_PANEL_SCORE].value = score;
4603
4604     DisplayGameControlValues();
4605
4606     if (time == time_final)
4607       StopSound(SND_GAME_LEVELTIME_BONUS);
4608     else if (setup.sound_loops)
4609       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4610     else
4611       PlaySound(SND_GAME_LEVELTIME_BONUS);
4612
4613     return;
4614   }
4615
4616   if (game_over_delay_2 > 0)
4617   {
4618     game_over_delay_2--;
4619
4620     return;
4621   }
4622
4623   if (health != health_final)
4624   {
4625     int health_count_dir = (health < health_final ? +1 : -1);
4626
4627     health += health_count_dir;
4628     score  += level.score[SC_TIME_BONUS];
4629
4630     local_player->LevelSolved_CountingHealth = health;
4631     local_player->LevelSolved_CountingScore = score;
4632
4633     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4634     game_panel_controls[GAME_PANEL_SCORE].value = score;
4635
4636     DisplayGameControlValues();
4637
4638     if (health == health_final)
4639       StopSound(SND_GAME_LEVELTIME_BONUS);
4640     else if (setup.sound_loops)
4641       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4642     else
4643       PlaySound(SND_GAME_LEVELTIME_BONUS);
4644
4645     return;
4646   }
4647
4648   local_player->LevelSolved_PanelOff = TRUE;
4649
4650   if (game_over_delay_3 > 0)
4651   {
4652     game_over_delay_3--;
4653
4654     return;
4655   }
4656
4657   GameEnd();
4658 }
4659
4660 void GameEnd()
4661 {
4662   int hi_pos;
4663
4664   local_player->LevelSolved_GameEnd = TRUE;
4665
4666   if (local_player->LevelSolved_SaveTape)
4667   {
4668     /* make sure that request dialog to save tape does not open door again */
4669     if (!global.use_envelope_request)
4670       CloseDoor(DOOR_CLOSE_1);
4671
4672     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4673   }
4674
4675   /* if no tape is to be saved, close both doors simultaneously */
4676   CloseDoor(DOOR_CLOSE_ALL);
4677
4678   if (level_editor_test_game)
4679   {
4680     SetGameStatus(GAME_MODE_MAIN);
4681
4682     DrawMainMenu();
4683
4684     return;
4685   }
4686
4687   if (!local_player->LevelSolved_SaveScore)
4688   {
4689     SetGameStatus(GAME_MODE_MAIN);
4690
4691     DrawMainMenu();
4692
4693     return;
4694   }
4695
4696   if (level_nr == leveldir_current->handicap_level)
4697   {
4698     leveldir_current->handicap_level++;
4699
4700     SaveLevelSetup_SeriesInfo();
4701   }
4702
4703   if (setup.increment_levels &&
4704       level_nr < leveldir_current->last_level &&
4705       !network_playing)
4706   {
4707     level_nr++;         /* advance to next level */
4708     TapeErase();        /* start with empty tape */
4709
4710     if (setup.auto_play_next_level)
4711     {
4712       LoadLevel(level_nr);
4713
4714       SaveLevelSetup_SeriesInfo();
4715     }
4716   }
4717
4718   /* used instead of last "level_nr" (for network games) */
4719   hi_pos = NewHiScore(levelset.level_nr);
4720
4721   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4722   {
4723     SetGameStatus(GAME_MODE_SCORES);
4724
4725     DrawHallOfFame(levelset.level_nr, hi_pos);
4726   }
4727   else if (setup.auto_play_next_level && setup.increment_levels &&
4728            !network_playing)
4729   {
4730     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4731   }
4732   else
4733   {
4734     SetGameStatus(GAME_MODE_MAIN);
4735
4736     DrawMainMenu();
4737   }
4738 }
4739
4740 int NewHiScore(int level_nr)
4741 {
4742   int k, l;
4743   int position = -1;
4744   boolean one_score_entry_per_name = !program.many_scores_per_name;
4745
4746   LoadScore(level_nr);
4747
4748   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4749       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4750     return -1;
4751
4752   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4753   {
4754     if (local_player->score_final > highscore[k].Score)
4755     {
4756       /* player has made it to the hall of fame */
4757
4758       if (k < MAX_SCORE_ENTRIES - 1)
4759       {
4760         int m = MAX_SCORE_ENTRIES - 1;
4761
4762         if (one_score_entry_per_name)
4763         {
4764           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4765             if (strEqual(setup.player_name, highscore[l].Name))
4766               m = l;
4767
4768           if (m == k)   /* player's new highscore overwrites his old one */
4769             goto put_into_list;
4770         }
4771
4772         for (l = m; l > k; l--)
4773         {
4774           strcpy(highscore[l].Name, highscore[l - 1].Name);
4775           highscore[l].Score = highscore[l - 1].Score;
4776         }
4777       }
4778
4779       put_into_list:
4780
4781       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4782       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4783       highscore[k].Score = local_player->score_final; 
4784       position = k;
4785
4786       break;
4787     }
4788     else if (one_score_entry_per_name &&
4789              !strncmp(setup.player_name, highscore[k].Name,
4790                       MAX_PLAYER_NAME_LEN))
4791       break;    /* player already there with a higher score */
4792   }
4793
4794   if (position >= 0) 
4795     SaveScore(level_nr);
4796
4797   return position;
4798 }
4799
4800 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4801 {
4802   int element = Feld[x][y];
4803   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4804   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4805   int horiz_move = (dx != 0);
4806   int sign = (horiz_move ? dx : dy);
4807   int step = sign * element_info[element].move_stepsize;
4808
4809   /* special values for move stepsize for spring and things on conveyor belt */
4810   if (horiz_move)
4811   {
4812     if (CAN_FALL(element) &&
4813         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4814       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4815     else if (element == EL_SPRING)
4816       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4817   }
4818
4819   return step;
4820 }
4821
4822 inline static int getElementMoveStepsize(int x, int y)
4823 {
4824   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4825 }
4826
4827 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4828 {
4829   if (player->GfxAction != action || player->GfxDir != dir)
4830   {
4831     player->GfxAction = action;
4832     player->GfxDir = dir;
4833     player->Frame = 0;
4834     player->StepFrame = 0;
4835   }
4836 }
4837
4838 static void ResetGfxFrame(int x, int y)
4839 {
4840   // profiling showed that "autotest" spends 10~20% of its time in this function
4841   if (DrawingDeactivatedField())
4842     return;
4843
4844   int element = Feld[x][y];
4845   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4846
4847   if (graphic_info[graphic].anim_global_sync)
4848     GfxFrame[x][y] = FrameCounter;
4849   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4850     GfxFrame[x][y] = CustomValue[x][y];
4851   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4852     GfxFrame[x][y] = element_info[element].collect_score;
4853   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4854     GfxFrame[x][y] = ChangeDelay[x][y];
4855 }
4856
4857 static void ResetGfxAnimation(int x, int y)
4858 {
4859   GfxAction[x][y] = ACTION_DEFAULT;
4860   GfxDir[x][y] = MovDir[x][y];
4861   GfxFrame[x][y] = 0;
4862
4863   ResetGfxFrame(x, y);
4864 }
4865
4866 static void ResetRandomAnimationValue(int x, int y)
4867 {
4868   GfxRandom[x][y] = INIT_GFX_RANDOM();
4869 }
4870
4871 void InitMovingField(int x, int y, int direction)
4872 {
4873   int element = Feld[x][y];
4874   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4875   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4876   int newx = x + dx;
4877   int newy = y + dy;
4878   boolean is_moving_before, is_moving_after;
4879
4880   /* check if element was/is moving or being moved before/after mode change */
4881   is_moving_before = (WasJustMoving[x][y] != 0);
4882   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4883
4884   /* reset animation only for moving elements which change direction of moving
4885      or which just started or stopped moving
4886      (else CEs with property "can move" / "not moving" are reset each frame) */
4887   if (is_moving_before != is_moving_after ||
4888       direction != MovDir[x][y])
4889     ResetGfxAnimation(x, y);
4890
4891   MovDir[x][y] = direction;
4892   GfxDir[x][y] = direction;
4893
4894   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4895                      direction == MV_DOWN && CAN_FALL(element) ?
4896                      ACTION_FALLING : ACTION_MOVING);
4897
4898   /* this is needed for CEs with property "can move" / "not moving" */
4899
4900   if (is_moving_after)
4901   {
4902     if (Feld[newx][newy] == EL_EMPTY)
4903       Feld[newx][newy] = EL_BLOCKED;
4904
4905     MovDir[newx][newy] = MovDir[x][y];
4906
4907     CustomValue[newx][newy] = CustomValue[x][y];
4908
4909     GfxFrame[newx][newy] = GfxFrame[x][y];
4910     GfxRandom[newx][newy] = GfxRandom[x][y];
4911     GfxAction[newx][newy] = GfxAction[x][y];
4912     GfxDir[newx][newy] = GfxDir[x][y];
4913   }
4914 }
4915
4916 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4917 {
4918   int direction = MovDir[x][y];
4919   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4920   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4921
4922   *goes_to_x = newx;
4923   *goes_to_y = newy;
4924 }
4925
4926 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4927 {
4928   int oldx = x, oldy = y;
4929   int direction = MovDir[x][y];
4930
4931   if (direction == MV_LEFT)
4932     oldx++;
4933   else if (direction == MV_RIGHT)
4934     oldx--;
4935   else if (direction == MV_UP)
4936     oldy++;
4937   else if (direction == MV_DOWN)
4938     oldy--;
4939
4940   *comes_from_x = oldx;
4941   *comes_from_y = oldy;
4942 }
4943
4944 int MovingOrBlocked2Element(int x, int y)
4945 {
4946   int element = Feld[x][y];
4947
4948   if (element == EL_BLOCKED)
4949   {
4950     int oldx, oldy;
4951
4952     Blocked2Moving(x, y, &oldx, &oldy);
4953     return Feld[oldx][oldy];
4954   }
4955   else
4956     return element;
4957 }
4958
4959 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4960 {
4961   /* like MovingOrBlocked2Element(), but if element is moving
4962      and (x,y) is the field the moving element is just leaving,
4963      return EL_BLOCKED instead of the element value */
4964   int element = Feld[x][y];
4965
4966   if (IS_MOVING(x, y))
4967   {
4968     if (element == EL_BLOCKED)
4969     {
4970       int oldx, oldy;
4971
4972       Blocked2Moving(x, y, &oldx, &oldy);
4973       return Feld[oldx][oldy];
4974     }
4975     else
4976       return EL_BLOCKED;
4977   }
4978   else
4979     return element;
4980 }
4981
4982 static void RemoveField(int x, int y)
4983 {
4984   Feld[x][y] = EL_EMPTY;
4985
4986   MovPos[x][y] = 0;
4987   MovDir[x][y] = 0;
4988   MovDelay[x][y] = 0;
4989
4990   CustomValue[x][y] = 0;
4991
4992   AmoebaNr[x][y] = 0;
4993   ChangeDelay[x][y] = 0;
4994   ChangePage[x][y] = -1;
4995   Pushed[x][y] = FALSE;
4996
4997   GfxElement[x][y] = EL_UNDEFINED;
4998   GfxAction[x][y] = ACTION_DEFAULT;
4999   GfxDir[x][y] = MV_NONE;
5000 }
5001
5002 void RemoveMovingField(int x, int y)
5003 {
5004   int oldx = x, oldy = y, newx = x, newy = y;
5005   int element = Feld[x][y];
5006   int next_element = EL_UNDEFINED;
5007
5008   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5009     return;
5010
5011   if (IS_MOVING(x, y))
5012   {
5013     Moving2Blocked(x, y, &newx, &newy);
5014
5015     if (Feld[newx][newy] != EL_BLOCKED)
5016     {
5017       /* element is moving, but target field is not free (blocked), but
5018          already occupied by something different (example: acid pool);
5019          in this case, only remove the moving field, but not the target */
5020
5021       RemoveField(oldx, oldy);
5022
5023       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5024
5025       TEST_DrawLevelField(oldx, oldy);
5026
5027       return;
5028     }
5029   }
5030   else if (element == EL_BLOCKED)
5031   {
5032     Blocked2Moving(x, y, &oldx, &oldy);
5033     if (!IS_MOVING(oldx, oldy))
5034       return;
5035   }
5036
5037   if (element == EL_BLOCKED &&
5038       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5039        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5040        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5041        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5042        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5043        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5044     next_element = get_next_element(Feld[oldx][oldy]);
5045
5046   RemoveField(oldx, oldy);
5047   RemoveField(newx, newy);
5048
5049   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5050
5051   if (next_element != EL_UNDEFINED)
5052     Feld[oldx][oldy] = next_element;
5053
5054   TEST_DrawLevelField(oldx, oldy);
5055   TEST_DrawLevelField(newx, newy);
5056 }
5057
5058 void DrawDynamite(int x, int y)
5059 {
5060   int sx = SCREENX(x), sy = SCREENY(y);
5061   int graphic = el2img(Feld[x][y]);
5062   int frame;
5063
5064   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5065     return;
5066
5067   if (IS_WALKABLE_INSIDE(Back[x][y]))
5068     return;
5069
5070   if (Back[x][y])
5071     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5072   else if (Store[x][y])
5073     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5074
5075   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5076
5077   if (Back[x][y] || Store[x][y])
5078     DrawGraphicThruMask(sx, sy, graphic, frame);
5079   else
5080     DrawGraphic(sx, sy, graphic, frame);
5081 }
5082
5083 void CheckDynamite(int x, int y)
5084 {
5085   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5086   {
5087     MovDelay[x][y]--;
5088
5089     if (MovDelay[x][y] != 0)
5090     {
5091       DrawDynamite(x, y);
5092       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5093
5094       return;
5095     }
5096   }
5097
5098   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5099
5100   Bang(x, y);
5101 }
5102
5103 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5104 {
5105   boolean num_checked_players = 0;
5106   int i;
5107
5108   for (i = 0; i < MAX_PLAYERS; i++)
5109   {
5110     if (stored_player[i].active)
5111     {
5112       int sx = stored_player[i].jx;
5113       int sy = stored_player[i].jy;
5114
5115       if (num_checked_players == 0)
5116       {
5117         *sx1 = *sx2 = sx;
5118         *sy1 = *sy2 = sy;
5119       }
5120       else
5121       {
5122         *sx1 = MIN(*sx1, sx);
5123         *sy1 = MIN(*sy1, sy);
5124         *sx2 = MAX(*sx2, sx);
5125         *sy2 = MAX(*sy2, sy);
5126       }
5127
5128       num_checked_players++;
5129     }
5130   }
5131 }
5132
5133 static boolean checkIfAllPlayersFitToScreen_RND()
5134 {
5135   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5136
5137   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5138
5139   return (sx2 - sx1 < SCR_FIELDX &&
5140           sy2 - sy1 < SCR_FIELDY);
5141 }
5142
5143 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5144 {
5145   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5146
5147   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5148
5149   *sx = (sx1 + sx2) / 2;
5150   *sy = (sy1 + sy2) / 2;
5151 }
5152
5153 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5154                         boolean center_screen, boolean quick_relocation)
5155 {
5156   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5157   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5158   boolean no_delay = (tape.warp_forward);
5159   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5160   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5161   int new_scroll_x, new_scroll_y;
5162
5163   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5164   {
5165     /* case 1: quick relocation inside visible screen (without scrolling) */
5166
5167     RedrawPlayfield();
5168
5169     return;
5170   }
5171
5172   if (!level.shifted_relocation || center_screen)
5173   {
5174     /* relocation _with_ centering of screen */
5175
5176     new_scroll_x = SCROLL_POSITION_X(x);
5177     new_scroll_y = SCROLL_POSITION_Y(y);
5178   }
5179   else
5180   {
5181     /* relocation _without_ centering of screen */
5182
5183     int center_scroll_x = SCROLL_POSITION_X(old_x);
5184     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5185     int offset_x = x + (scroll_x - center_scroll_x);
5186     int offset_y = y + (scroll_y - center_scroll_y);
5187
5188     /* for new screen position, apply previous offset to center position */
5189     new_scroll_x = SCROLL_POSITION_X(offset_x);
5190     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5191   }
5192
5193   if (quick_relocation)
5194   {
5195     /* case 2: quick relocation (redraw without visible scrolling) */
5196
5197     scroll_x = new_scroll_x;
5198     scroll_y = new_scroll_y;
5199
5200     RedrawPlayfield();
5201
5202     return;
5203   }
5204
5205   /* case 3: visible relocation (with scrolling to new position) */
5206
5207   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5208
5209   SetVideoFrameDelay(wait_delay_value);
5210
5211   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5212   {
5213     int dx = 0, dy = 0;
5214     int fx = FX, fy = FY;
5215
5216     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5217     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5218
5219     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5220       break;
5221
5222     scroll_x -= dx;
5223     scroll_y -= dy;
5224
5225     fx += dx * TILEX / 2;
5226     fy += dy * TILEY / 2;
5227
5228     ScrollLevel(dx, dy);
5229     DrawAllPlayers();
5230
5231     /* scroll in two steps of half tile size to make things smoother */
5232     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5233
5234     /* scroll second step to align at full tile size */
5235     BlitScreenToBitmap(window);
5236   }
5237
5238   DrawAllPlayers();
5239   BackToFront();
5240
5241   SetVideoFrameDelay(frame_delay_value_old);
5242 }
5243
5244 void RelocatePlayer(int jx, int jy, int el_player_raw)
5245 {
5246   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5247   int player_nr = GET_PLAYER_NR(el_player);
5248   struct PlayerInfo *player = &stored_player[player_nr];
5249   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5250   boolean no_delay = (tape.warp_forward);
5251   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5252   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5253   int old_jx = player->jx;
5254   int old_jy = player->jy;
5255   int old_element = Feld[old_jx][old_jy];
5256   int element = Feld[jx][jy];
5257   boolean player_relocated = (old_jx != jx || old_jy != jy);
5258
5259   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5260   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5261   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5262   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5263   int leave_side_horiz = move_dir_horiz;
5264   int leave_side_vert  = move_dir_vert;
5265   int enter_side = enter_side_horiz | enter_side_vert;
5266   int leave_side = leave_side_horiz | leave_side_vert;
5267
5268   if (player->GameOver)         /* do not reanimate dead player */
5269     return;
5270
5271   if (!player_relocated)        /* no need to relocate the player */
5272     return;
5273
5274   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5275   {
5276     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5277     DrawLevelField(jx, jy);
5278   }
5279
5280   if (player->present)
5281   {
5282     while (player->MovPos)
5283     {
5284       ScrollPlayer(player, SCROLL_GO_ON);
5285       ScrollScreen(NULL, SCROLL_GO_ON);
5286
5287       AdvanceFrameAndPlayerCounters(player->index_nr);
5288
5289       DrawPlayer(player);
5290
5291       BackToFront_WithFrameDelay(wait_delay_value);
5292     }
5293
5294     DrawPlayer(player);         /* needed here only to cleanup last field */
5295     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5296
5297     player->is_moving = FALSE;
5298   }
5299
5300   if (IS_CUSTOM_ELEMENT(old_element))
5301     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5302                                CE_LEFT_BY_PLAYER,
5303                                player->index_bit, leave_side);
5304
5305   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5306                                       CE_PLAYER_LEAVES_X,
5307                                       player->index_bit, leave_side);
5308
5309   Feld[jx][jy] = el_player;
5310   InitPlayerField(jx, jy, el_player, TRUE);
5311
5312   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5313      possible that the relocation target field did not contain a player element,
5314      but a walkable element, to which the new player was relocated -- in this
5315      case, restore that (already initialized!) element on the player field */
5316   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5317   {
5318     Feld[jx][jy] = element;     /* restore previously existing element */
5319   }
5320
5321   /* only visually relocate centered player */
5322   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5323                      FALSE, level.instant_relocation);
5324
5325   TestIfPlayerTouchesBadThing(jx, jy);
5326   TestIfPlayerTouchesCustomElement(jx, jy);
5327
5328   if (IS_CUSTOM_ELEMENT(element))
5329     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5330                                player->index_bit, enter_side);
5331
5332   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5333                                       player->index_bit, enter_side);
5334
5335   if (player->is_switching)
5336   {
5337     /* ensure that relocation while still switching an element does not cause
5338        a new element to be treated as also switched directly after relocation
5339        (this is important for teleporter switches that teleport the player to
5340        a place where another teleporter switch is in the same direction, which
5341        would then incorrectly be treated as immediately switched before the
5342        direction key that caused the switch was released) */
5343
5344     player->switch_x += jx - old_jx;
5345     player->switch_y += jy - old_jy;
5346   }
5347 }
5348
5349 void Explode(int ex, int ey, int phase, int mode)
5350 {
5351   int x, y;
5352   int last_phase;
5353   int border_element;
5354
5355   /* !!! eliminate this variable !!! */
5356   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5357
5358   if (game.explosions_delayed)
5359   {
5360     ExplodeField[ex][ey] = mode;
5361     return;
5362   }
5363
5364   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5365   {
5366     int center_element = Feld[ex][ey];
5367     int artwork_element, explosion_element;     /* set these values later */
5368
5369     /* remove things displayed in background while burning dynamite */
5370     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5371       Back[ex][ey] = 0;
5372
5373     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5374     {
5375       /* put moving element to center field (and let it explode there) */
5376       center_element = MovingOrBlocked2Element(ex, ey);
5377       RemoveMovingField(ex, ey);
5378       Feld[ex][ey] = center_element;
5379     }
5380
5381     /* now "center_element" is finally determined -- set related values now */
5382     artwork_element = center_element;           /* for custom player artwork */
5383     explosion_element = center_element;         /* for custom player artwork */
5384
5385     if (IS_PLAYER(ex, ey))
5386     {
5387       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5388
5389       artwork_element = stored_player[player_nr].artwork_element;
5390
5391       if (level.use_explosion_element[player_nr])
5392       {
5393         explosion_element = level.explosion_element[player_nr];
5394         artwork_element = explosion_element;
5395       }
5396     }
5397
5398     if (mode == EX_TYPE_NORMAL ||
5399         mode == EX_TYPE_CENTER ||
5400         mode == EX_TYPE_CROSS)
5401       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5402
5403     last_phase = element_info[explosion_element].explosion_delay + 1;
5404
5405     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5406     {
5407       int xx = x - ex + 1;
5408       int yy = y - ey + 1;
5409       int element;
5410
5411       if (!IN_LEV_FIELD(x, y) ||
5412           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5413           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5414         continue;
5415
5416       element = Feld[x][y];
5417
5418       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5419       {
5420         element = MovingOrBlocked2Element(x, y);
5421
5422         if (!IS_EXPLOSION_PROOF(element))
5423           RemoveMovingField(x, y);
5424       }
5425
5426       /* indestructible elements can only explode in center (but not flames) */
5427       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5428                                            mode == EX_TYPE_BORDER)) ||
5429           element == EL_FLAMES)
5430         continue;
5431
5432       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5433          behaviour, for example when touching a yamyam that explodes to rocks
5434          with active deadly shield, a rock is created under the player !!! */
5435       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5436 #if 0
5437       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5438           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5439            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5440 #else
5441       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5442 #endif
5443       {
5444         if (IS_ACTIVE_BOMB(element))
5445         {
5446           /* re-activate things under the bomb like gate or penguin */
5447           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5448           Back[x][y] = 0;
5449         }
5450
5451         continue;
5452       }
5453
5454       /* save walkable background elements while explosion on same tile */
5455       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5456           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5457         Back[x][y] = element;
5458
5459       /* ignite explodable elements reached by other explosion */
5460       if (element == EL_EXPLOSION)
5461         element = Store2[x][y];
5462
5463       if (AmoebaNr[x][y] &&
5464           (element == EL_AMOEBA_FULL ||
5465            element == EL_BD_AMOEBA ||
5466            element == EL_AMOEBA_GROWING))
5467       {
5468         AmoebaCnt[AmoebaNr[x][y]]--;
5469         AmoebaCnt2[AmoebaNr[x][y]]--;
5470       }
5471
5472       RemoveField(x, y);
5473
5474       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5475       {
5476         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5477
5478         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5479
5480         if (PLAYERINFO(ex, ey)->use_murphy)
5481           Store[x][y] = EL_EMPTY;
5482       }
5483
5484       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5485          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5486       else if (ELEM_IS_PLAYER(center_element))
5487         Store[x][y] = EL_EMPTY;
5488       else if (center_element == EL_YAMYAM)
5489         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5490       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5491         Store[x][y] = element_info[center_element].content.e[xx][yy];
5492 #if 1
5493       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5494          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5495          otherwise) -- FIX THIS !!! */
5496       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5497         Store[x][y] = element_info[element].content.e[1][1];
5498 #else
5499       else if (!CAN_EXPLODE(element))
5500         Store[x][y] = element_info[element].content.e[1][1];
5501 #endif
5502       else
5503         Store[x][y] = EL_EMPTY;
5504
5505       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5506           center_element == EL_AMOEBA_TO_DIAMOND)
5507         Store2[x][y] = element;
5508
5509       Feld[x][y] = EL_EXPLOSION;
5510       GfxElement[x][y] = artwork_element;
5511
5512       ExplodePhase[x][y] = 1;
5513       ExplodeDelay[x][y] = last_phase;
5514
5515       Stop[x][y] = TRUE;
5516     }
5517
5518     if (center_element == EL_YAMYAM)
5519       game.yamyam_content_nr =
5520         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5521
5522     return;
5523   }
5524
5525   if (Stop[ex][ey])
5526     return;
5527
5528   x = ex;
5529   y = ey;
5530
5531   if (phase == 1)
5532     GfxFrame[x][y] = 0;         /* restart explosion animation */
5533
5534   last_phase = ExplodeDelay[x][y];
5535
5536   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5537
5538   /* this can happen if the player leaves an explosion just in time */
5539   if (GfxElement[x][y] == EL_UNDEFINED)
5540     GfxElement[x][y] = EL_EMPTY;
5541
5542   border_element = Store2[x][y];
5543   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5544     border_element = StorePlayer[x][y];
5545
5546   if (phase == element_info[border_element].ignition_delay ||
5547       phase == last_phase)
5548   {
5549     boolean border_explosion = FALSE;
5550
5551     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5552         !PLAYER_EXPLOSION_PROTECTED(x, y))
5553     {
5554       KillPlayerUnlessExplosionProtected(x, y);
5555       border_explosion = TRUE;
5556     }
5557     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5558     {
5559       Feld[x][y] = Store2[x][y];
5560       Store2[x][y] = 0;
5561       Bang(x, y);
5562       border_explosion = TRUE;
5563     }
5564     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5565     {
5566       AmoebeUmwandeln(x, y);
5567       Store2[x][y] = 0;
5568       border_explosion = TRUE;
5569     }
5570
5571     /* if an element just explodes due to another explosion (chain-reaction),
5572        do not immediately end the new explosion when it was the last frame of
5573        the explosion (as it would be done in the following "if"-statement!) */
5574     if (border_explosion && phase == last_phase)
5575       return;
5576   }
5577
5578   if (phase == last_phase)
5579   {
5580     int element;
5581
5582     element = Feld[x][y] = Store[x][y];
5583     Store[x][y] = Store2[x][y] = 0;
5584     GfxElement[x][y] = EL_UNDEFINED;
5585
5586     /* player can escape from explosions and might therefore be still alive */
5587     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5588         element <= EL_PLAYER_IS_EXPLODING_4)
5589     {
5590       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5591       int explosion_element = EL_PLAYER_1 + player_nr;
5592       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5593       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5594
5595       if (level.use_explosion_element[player_nr])
5596         explosion_element = level.explosion_element[player_nr];
5597
5598       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5599                     element_info[explosion_element].content.e[xx][yy]);
5600     }
5601
5602     /* restore probably existing indestructible background element */
5603     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5604       element = Feld[x][y] = Back[x][y];
5605     Back[x][y] = 0;
5606
5607     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5608     GfxDir[x][y] = MV_NONE;
5609     ChangeDelay[x][y] = 0;
5610     ChangePage[x][y] = -1;
5611
5612     CustomValue[x][y] = 0;
5613
5614     InitField_WithBug2(x, y, FALSE);
5615
5616     TEST_DrawLevelField(x, y);
5617
5618     TestIfElementTouchesCustomElement(x, y);
5619
5620     if (GFX_CRUMBLED(element))
5621       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5622
5623     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5624       StorePlayer[x][y] = 0;
5625
5626     if (ELEM_IS_PLAYER(element))
5627       RelocatePlayer(x, y, element);
5628   }
5629   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5630   {
5631     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5632     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5633
5634     if (phase == delay)
5635       TEST_DrawLevelFieldCrumbled(x, y);
5636
5637     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5638     {
5639       DrawLevelElement(x, y, Back[x][y]);
5640       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5641     }
5642     else if (IS_WALKABLE_UNDER(Back[x][y]))
5643     {
5644       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5645       DrawLevelElementThruMask(x, y, Back[x][y]);
5646     }
5647     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5648       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5649   }
5650 }
5651
5652 void DynaExplode(int ex, int ey)
5653 {
5654   int i, j;
5655   int dynabomb_element = Feld[ex][ey];
5656   int dynabomb_size = 1;
5657   boolean dynabomb_xl = FALSE;
5658   struct PlayerInfo *player;
5659   static int xy[4][2] =
5660   {
5661     { 0, -1 },
5662     { -1, 0 },
5663     { +1, 0 },
5664     { 0, +1 }
5665   };
5666
5667   if (IS_ACTIVE_BOMB(dynabomb_element))
5668   {
5669     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5670     dynabomb_size = player->dynabomb_size;
5671     dynabomb_xl = player->dynabomb_xl;
5672     player->dynabombs_left++;
5673   }
5674
5675   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5676
5677   for (i = 0; i < NUM_DIRECTIONS; i++)
5678   {
5679     for (j = 1; j <= dynabomb_size; j++)
5680     {
5681       int x = ex + j * xy[i][0];
5682       int y = ey + j * xy[i][1];
5683       int element;
5684
5685       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5686         break;
5687
5688       element = Feld[x][y];
5689
5690       /* do not restart explosions of fields with active bombs */
5691       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5692         continue;
5693
5694       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5695
5696       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5697           !IS_DIGGABLE(element) && !dynabomb_xl)
5698         break;
5699     }
5700   }
5701 }
5702
5703 void Bang(int x, int y)
5704 {
5705   int element = MovingOrBlocked2Element(x, y);
5706   int explosion_type = EX_TYPE_NORMAL;
5707
5708   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5709   {
5710     struct PlayerInfo *player = PLAYERINFO(x, y);
5711
5712     element = Feld[x][y] = player->initial_element;
5713
5714     if (level.use_explosion_element[player->index_nr])
5715     {
5716       int explosion_element = level.explosion_element[player->index_nr];
5717
5718       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5719         explosion_type = EX_TYPE_CROSS;
5720       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5721         explosion_type = EX_TYPE_CENTER;
5722     }
5723   }
5724
5725   switch (element)
5726   {
5727     case EL_BUG:
5728     case EL_SPACESHIP:
5729     case EL_BD_BUTTERFLY:
5730     case EL_BD_FIREFLY:
5731     case EL_YAMYAM:
5732     case EL_DARK_YAMYAM:
5733     case EL_ROBOT:
5734     case EL_PACMAN:
5735     case EL_MOLE:
5736       RaiseScoreElement(element);
5737       break;
5738
5739     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5740     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5741     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5742     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5743     case EL_DYNABOMB_INCREASE_NUMBER:
5744     case EL_DYNABOMB_INCREASE_SIZE:
5745     case EL_DYNABOMB_INCREASE_POWER:
5746       explosion_type = EX_TYPE_DYNA;
5747       break;
5748
5749     case EL_DC_LANDMINE:
5750       explosion_type = EX_TYPE_CENTER;
5751       break;
5752
5753     case EL_PENGUIN:
5754     case EL_LAMP:
5755     case EL_LAMP_ACTIVE:
5756     case EL_AMOEBA_TO_DIAMOND:
5757       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5758         explosion_type = EX_TYPE_CENTER;
5759       break;
5760
5761     default:
5762       if (element_info[element].explosion_type == EXPLODES_CROSS)
5763         explosion_type = EX_TYPE_CROSS;
5764       else if (element_info[element].explosion_type == EXPLODES_1X1)
5765         explosion_type = EX_TYPE_CENTER;
5766       break;
5767   }
5768
5769   if (explosion_type == EX_TYPE_DYNA)
5770     DynaExplode(x, y);
5771   else
5772     Explode(x, y, EX_PHASE_START, explosion_type);
5773
5774   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5775 }
5776
5777 void SplashAcid(int x, int y)
5778 {
5779   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5780       (!IN_LEV_FIELD(x - 1, y - 2) ||
5781        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5782     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5783
5784   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5785       (!IN_LEV_FIELD(x + 1, y - 2) ||
5786        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5787     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5788
5789   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5790 }
5791
5792 static void InitBeltMovement()
5793 {
5794   static int belt_base_element[4] =
5795   {
5796     EL_CONVEYOR_BELT_1_LEFT,
5797     EL_CONVEYOR_BELT_2_LEFT,
5798     EL_CONVEYOR_BELT_3_LEFT,
5799     EL_CONVEYOR_BELT_4_LEFT
5800   };
5801   static int belt_base_active_element[4] =
5802   {
5803     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5804     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5805     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5806     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5807   };
5808
5809   int x, y, i, j;
5810
5811   /* set frame order for belt animation graphic according to belt direction */
5812   for (i = 0; i < NUM_BELTS; i++)
5813   {
5814     int belt_nr = i;
5815
5816     for (j = 0; j < NUM_BELT_PARTS; j++)
5817     {
5818       int element = belt_base_active_element[belt_nr] + j;
5819       int graphic_1 = el2img(element);
5820       int graphic_2 = el2panelimg(element);
5821
5822       if (game.belt_dir[i] == MV_LEFT)
5823       {
5824         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5825         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5826       }
5827       else
5828       {
5829         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5830         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5831       }
5832     }
5833   }
5834
5835   SCAN_PLAYFIELD(x, y)
5836   {
5837     int element = Feld[x][y];
5838
5839     for (i = 0; i < NUM_BELTS; i++)
5840     {
5841       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5842       {
5843         int e_belt_nr = getBeltNrFromBeltElement(element);
5844         int belt_nr = i;
5845
5846         if (e_belt_nr == belt_nr)
5847         {
5848           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5849
5850           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5851         }
5852       }
5853     }
5854   }
5855 }
5856
5857 static void ToggleBeltSwitch(int x, int y)
5858 {
5859   static int belt_base_element[4] =
5860   {
5861     EL_CONVEYOR_BELT_1_LEFT,
5862     EL_CONVEYOR_BELT_2_LEFT,
5863     EL_CONVEYOR_BELT_3_LEFT,
5864     EL_CONVEYOR_BELT_4_LEFT
5865   };
5866   static int belt_base_active_element[4] =
5867   {
5868     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5869     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5870     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5871     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5872   };
5873   static int belt_base_switch_element[4] =
5874   {
5875     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5876     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5877     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5878     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5879   };
5880   static int belt_move_dir[4] =
5881   {
5882     MV_LEFT,
5883     MV_NONE,
5884     MV_RIGHT,
5885     MV_NONE,
5886   };
5887
5888   int element = Feld[x][y];
5889   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5890   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5891   int belt_dir = belt_move_dir[belt_dir_nr];
5892   int xx, yy, i;
5893
5894   if (!IS_BELT_SWITCH(element))
5895     return;
5896
5897   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5898   game.belt_dir[belt_nr] = belt_dir;
5899
5900   if (belt_dir_nr == 3)
5901     belt_dir_nr = 1;
5902
5903   /* set frame order for belt animation graphic according to belt direction */
5904   for (i = 0; i < NUM_BELT_PARTS; i++)
5905   {
5906     int element = belt_base_active_element[belt_nr] + i;
5907     int graphic_1 = el2img(element);
5908     int graphic_2 = el2panelimg(element);
5909
5910     if (belt_dir == MV_LEFT)
5911     {
5912       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5913       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5914     }
5915     else
5916     {
5917       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5918       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5919     }
5920   }
5921
5922   SCAN_PLAYFIELD(xx, yy)
5923   {
5924     int element = Feld[xx][yy];
5925
5926     if (IS_BELT_SWITCH(element))
5927     {
5928       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5929
5930       if (e_belt_nr == belt_nr)
5931       {
5932         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5933         TEST_DrawLevelField(xx, yy);
5934       }
5935     }
5936     else if (IS_BELT(element) && belt_dir != MV_NONE)
5937     {
5938       int e_belt_nr = getBeltNrFromBeltElement(element);
5939
5940       if (e_belt_nr == belt_nr)
5941       {
5942         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5943
5944         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5945         TEST_DrawLevelField(xx, yy);
5946       }
5947     }
5948     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5949     {
5950       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5951
5952       if (e_belt_nr == belt_nr)
5953       {
5954         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5955
5956         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5957         TEST_DrawLevelField(xx, yy);
5958       }
5959     }
5960   }
5961 }
5962
5963 static void ToggleSwitchgateSwitch(int x, int y)
5964 {
5965   int xx, yy;
5966
5967   game.switchgate_pos = !game.switchgate_pos;
5968
5969   SCAN_PLAYFIELD(xx, yy)
5970   {
5971     int element = Feld[xx][yy];
5972
5973     if (element == EL_SWITCHGATE_SWITCH_UP)
5974     {
5975       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5976       TEST_DrawLevelField(xx, yy);
5977     }
5978     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5979     {
5980       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5981       TEST_DrawLevelField(xx, yy);
5982     }
5983     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5984     {
5985       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5986       TEST_DrawLevelField(xx, yy);
5987     }
5988     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5989     {
5990       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5991       TEST_DrawLevelField(xx, yy);
5992     }
5993     else if (element == EL_SWITCHGATE_OPEN ||
5994              element == EL_SWITCHGATE_OPENING)
5995     {
5996       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5997
5998       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5999     }
6000     else if (element == EL_SWITCHGATE_CLOSED ||
6001              element == EL_SWITCHGATE_CLOSING)
6002     {
6003       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6004
6005       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6006     }
6007   }
6008 }
6009
6010 static int getInvisibleActiveFromInvisibleElement(int element)
6011 {
6012   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6013           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6014           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6015           element);
6016 }
6017
6018 static int getInvisibleFromInvisibleActiveElement(int element)
6019 {
6020   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6021           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6022           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6023           element);
6024 }
6025
6026 static void RedrawAllLightSwitchesAndInvisibleElements()
6027 {
6028   int x, y;
6029
6030   SCAN_PLAYFIELD(x, y)
6031   {
6032     int element = Feld[x][y];
6033
6034     if (element == EL_LIGHT_SWITCH &&
6035         game.light_time_left > 0)
6036     {
6037       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6038       TEST_DrawLevelField(x, y);
6039     }
6040     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6041              game.light_time_left == 0)
6042     {
6043       Feld[x][y] = EL_LIGHT_SWITCH;
6044       TEST_DrawLevelField(x, y);
6045     }
6046     else if (element == EL_EMC_DRIPPER &&
6047              game.light_time_left > 0)
6048     {
6049       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6050       TEST_DrawLevelField(x, y);
6051     }
6052     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6053              game.light_time_left == 0)
6054     {
6055       Feld[x][y] = EL_EMC_DRIPPER;
6056       TEST_DrawLevelField(x, y);
6057     }
6058     else if (element == EL_INVISIBLE_STEELWALL ||
6059              element == EL_INVISIBLE_WALL ||
6060              element == EL_INVISIBLE_SAND)
6061     {
6062       if (game.light_time_left > 0)
6063         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6064
6065       TEST_DrawLevelField(x, y);
6066
6067       /* uncrumble neighbour fields, if needed */
6068       if (element == EL_INVISIBLE_SAND)
6069         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6070     }
6071     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6072              element == EL_INVISIBLE_WALL_ACTIVE ||
6073              element == EL_INVISIBLE_SAND_ACTIVE)
6074     {
6075       if (game.light_time_left == 0)
6076         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6077
6078       TEST_DrawLevelField(x, y);
6079
6080       /* re-crumble neighbour fields, if needed */
6081       if (element == EL_INVISIBLE_SAND)
6082         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6083     }
6084   }
6085 }
6086
6087 static void RedrawAllInvisibleElementsForLenses()
6088 {
6089   int x, y;
6090
6091   SCAN_PLAYFIELD(x, y)
6092   {
6093     int element = Feld[x][y];
6094
6095     if (element == EL_EMC_DRIPPER &&
6096         game.lenses_time_left > 0)
6097     {
6098       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6099       TEST_DrawLevelField(x, y);
6100     }
6101     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6102              game.lenses_time_left == 0)
6103     {
6104       Feld[x][y] = EL_EMC_DRIPPER;
6105       TEST_DrawLevelField(x, y);
6106     }
6107     else if (element == EL_INVISIBLE_STEELWALL ||
6108              element == EL_INVISIBLE_WALL ||
6109              element == EL_INVISIBLE_SAND)
6110     {
6111       if (game.lenses_time_left > 0)
6112         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6113
6114       TEST_DrawLevelField(x, y);
6115
6116       /* uncrumble neighbour fields, if needed */
6117       if (element == EL_INVISIBLE_SAND)
6118         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6119     }
6120     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6121              element == EL_INVISIBLE_WALL_ACTIVE ||
6122              element == EL_INVISIBLE_SAND_ACTIVE)
6123     {
6124       if (game.lenses_time_left == 0)
6125         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6126
6127       TEST_DrawLevelField(x, y);
6128
6129       /* re-crumble neighbour fields, if needed */
6130       if (element == EL_INVISIBLE_SAND)
6131         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6132     }
6133   }
6134 }
6135
6136 static void RedrawAllInvisibleElementsForMagnifier()
6137 {
6138   int x, y;
6139
6140   SCAN_PLAYFIELD(x, y)
6141   {
6142     int element = Feld[x][y];
6143
6144     if (element == EL_EMC_FAKE_GRASS &&
6145         game.magnify_time_left > 0)
6146     {
6147       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6148       TEST_DrawLevelField(x, y);
6149     }
6150     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6151              game.magnify_time_left == 0)
6152     {
6153       Feld[x][y] = EL_EMC_FAKE_GRASS;
6154       TEST_DrawLevelField(x, y);
6155     }
6156     else if (IS_GATE_GRAY(element) &&
6157              game.magnify_time_left > 0)
6158     {
6159       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6160                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6161                     IS_EM_GATE_GRAY(element) ?
6162                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6163                     IS_EMC_GATE_GRAY(element) ?
6164                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6165                     IS_DC_GATE_GRAY(element) ?
6166                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6167                     element);
6168       TEST_DrawLevelField(x, y);
6169     }
6170     else if (IS_GATE_GRAY_ACTIVE(element) &&
6171              game.magnify_time_left == 0)
6172     {
6173       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6174                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6175                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6176                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6177                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6178                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6179                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6180                     EL_DC_GATE_WHITE_GRAY :
6181                     element);
6182       TEST_DrawLevelField(x, y);
6183     }
6184   }
6185 }
6186
6187 static void ToggleLightSwitch(int x, int y)
6188 {
6189   int element = Feld[x][y];
6190
6191   game.light_time_left =
6192     (element == EL_LIGHT_SWITCH ?
6193      level.time_light * FRAMES_PER_SECOND : 0);
6194
6195   RedrawAllLightSwitchesAndInvisibleElements();
6196 }
6197
6198 static void ActivateTimegateSwitch(int x, int y)
6199 {
6200   int xx, yy;
6201
6202   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6203
6204   SCAN_PLAYFIELD(xx, yy)
6205   {
6206     int element = Feld[xx][yy];
6207
6208     if (element == EL_TIMEGATE_CLOSED ||
6209         element == EL_TIMEGATE_CLOSING)
6210     {
6211       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6212       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6213     }
6214
6215     /*
6216     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6217     {
6218       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6219       TEST_DrawLevelField(xx, yy);
6220     }
6221     */
6222
6223   }
6224
6225   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6226                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6227 }
6228
6229 void Impact(int x, int y)
6230 {
6231   boolean last_line = (y == lev_fieldy - 1);
6232   boolean object_hit = FALSE;
6233   boolean impact = (last_line || object_hit);
6234   int element = Feld[x][y];
6235   int smashed = EL_STEELWALL;
6236
6237   if (!last_line)       /* check if element below was hit */
6238   {
6239     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6240       return;
6241
6242     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6243                                          MovDir[x][y + 1] != MV_DOWN ||
6244                                          MovPos[x][y + 1] <= TILEY / 2));
6245
6246     /* do not smash moving elements that left the smashed field in time */
6247     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6248         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6249       object_hit = FALSE;
6250
6251 #if USE_QUICKSAND_IMPACT_BUGFIX
6252     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6253     {
6254       RemoveMovingField(x, y + 1);
6255       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6256       Feld[x][y + 2] = EL_ROCK;
6257       TEST_DrawLevelField(x, y + 2);
6258
6259       object_hit = TRUE;
6260     }
6261
6262     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6263     {
6264       RemoveMovingField(x, y + 1);
6265       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6266       Feld[x][y + 2] = EL_ROCK;
6267       TEST_DrawLevelField(x, y + 2);
6268
6269       object_hit = TRUE;
6270     }
6271 #endif
6272
6273     if (object_hit)
6274       smashed = MovingOrBlocked2Element(x, y + 1);
6275
6276     impact = (last_line || object_hit);
6277   }
6278
6279   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6280   {
6281     SplashAcid(x, y + 1);
6282     return;
6283   }
6284
6285   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6286   /* only reset graphic animation if graphic really changes after impact */
6287   if (impact &&
6288       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6289   {
6290     ResetGfxAnimation(x, y);
6291     TEST_DrawLevelField(x, y);
6292   }
6293
6294   if (impact && CAN_EXPLODE_IMPACT(element))
6295   {
6296     Bang(x, y);
6297     return;
6298   }
6299   else if (impact && element == EL_PEARL &&
6300            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6301   {
6302     ResetGfxAnimation(x, y);
6303
6304     Feld[x][y] = EL_PEARL_BREAKING;
6305     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6306     return;
6307   }
6308   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6309   {
6310     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6311
6312     return;
6313   }
6314
6315   if (impact && element == EL_AMOEBA_DROP)
6316   {
6317     if (object_hit && IS_PLAYER(x, y + 1))
6318       KillPlayerUnlessEnemyProtected(x, y + 1);
6319     else if (object_hit && smashed == EL_PENGUIN)
6320       Bang(x, y + 1);
6321     else
6322     {
6323       Feld[x][y] = EL_AMOEBA_GROWING;
6324       Store[x][y] = EL_AMOEBA_WET;
6325
6326       ResetRandomAnimationValue(x, y);
6327     }
6328     return;
6329   }
6330
6331   if (object_hit)               /* check which object was hit */
6332   {
6333     if ((CAN_PASS_MAGIC_WALL(element) && 
6334          (smashed == EL_MAGIC_WALL ||
6335           smashed == EL_BD_MAGIC_WALL)) ||
6336         (CAN_PASS_DC_MAGIC_WALL(element) &&
6337          smashed == EL_DC_MAGIC_WALL))
6338     {
6339       int xx, yy;
6340       int activated_magic_wall =
6341         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6342          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6343          EL_DC_MAGIC_WALL_ACTIVE);
6344
6345       /* activate magic wall / mill */
6346       SCAN_PLAYFIELD(xx, yy)
6347       {
6348         if (Feld[xx][yy] == smashed)
6349           Feld[xx][yy] = activated_magic_wall;
6350       }
6351
6352       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6353       game.magic_wall_active = TRUE;
6354
6355       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6356                             SND_MAGIC_WALL_ACTIVATING :
6357                             smashed == EL_BD_MAGIC_WALL ?
6358                             SND_BD_MAGIC_WALL_ACTIVATING :
6359                             SND_DC_MAGIC_WALL_ACTIVATING));
6360     }
6361
6362     if (IS_PLAYER(x, y + 1))
6363     {
6364       if (CAN_SMASH_PLAYER(element))
6365       {
6366         KillPlayerUnlessEnemyProtected(x, y + 1);
6367         return;
6368       }
6369     }
6370     else if (smashed == EL_PENGUIN)
6371     {
6372       if (CAN_SMASH_PLAYER(element))
6373       {
6374         Bang(x, y + 1);
6375         return;
6376       }
6377     }
6378     else if (element == EL_BD_DIAMOND)
6379     {
6380       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6381       {
6382         Bang(x, y + 1);
6383         return;
6384       }
6385     }
6386     else if (((element == EL_SP_INFOTRON ||
6387                element == EL_SP_ZONK) &&
6388               (smashed == EL_SP_SNIKSNAK ||
6389                smashed == EL_SP_ELECTRON ||
6390                smashed == EL_SP_DISK_ORANGE)) ||
6391              (element == EL_SP_INFOTRON &&
6392               smashed == EL_SP_DISK_YELLOW))
6393     {
6394       Bang(x, y + 1);
6395       return;
6396     }
6397     else if (CAN_SMASH_EVERYTHING(element))
6398     {
6399       if (IS_CLASSIC_ENEMY(smashed) ||
6400           CAN_EXPLODE_SMASHED(smashed))
6401       {
6402         Bang(x, y + 1);
6403         return;
6404       }
6405       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6406       {
6407         if (smashed == EL_LAMP ||
6408             smashed == EL_LAMP_ACTIVE)
6409         {
6410           Bang(x, y + 1);
6411           return;
6412         }
6413         else if (smashed == EL_NUT)
6414         {
6415           Feld[x][y + 1] = EL_NUT_BREAKING;
6416           PlayLevelSound(x, y, SND_NUT_BREAKING);
6417           RaiseScoreElement(EL_NUT);
6418           return;
6419         }
6420         else if (smashed == EL_PEARL)
6421         {
6422           ResetGfxAnimation(x, y);
6423
6424           Feld[x][y + 1] = EL_PEARL_BREAKING;
6425           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6426           return;
6427         }
6428         else if (smashed == EL_DIAMOND)
6429         {
6430           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6431           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6432           return;
6433         }
6434         else if (IS_BELT_SWITCH(smashed))
6435         {
6436           ToggleBeltSwitch(x, y + 1);
6437         }
6438         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6439                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6440                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6441                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6442         {
6443           ToggleSwitchgateSwitch(x, y + 1);
6444         }
6445         else if (smashed == EL_LIGHT_SWITCH ||
6446                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6447         {
6448           ToggleLightSwitch(x, y + 1);
6449         }
6450         else
6451         {
6452           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6453
6454           CheckElementChangeBySide(x, y + 1, smashed, element,
6455                                    CE_SWITCHED, CH_SIDE_TOP);
6456           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6457                                             CH_SIDE_TOP);
6458         }
6459       }
6460       else
6461       {
6462         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6463       }
6464     }
6465   }
6466
6467   /* play sound of magic wall / mill */
6468   if (!last_line &&
6469       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6470        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6471        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6472   {
6473     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6474       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6475     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6476       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6477     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6478       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6479
6480     return;
6481   }
6482
6483   /* play sound of object that hits the ground */
6484   if (last_line || object_hit)
6485     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6486 }
6487
6488 inline static void TurnRoundExt(int x, int y)
6489 {
6490   static struct
6491   {
6492     int dx, dy;
6493   } move_xy[] =
6494   {
6495     {  0,  0 },
6496     { -1,  0 },
6497     { +1,  0 },
6498     {  0,  0 },
6499     {  0, -1 },
6500     {  0,  0 }, { 0, 0 }, { 0, 0 },
6501     {  0, +1 }
6502   };
6503   static struct
6504   {
6505     int left, right, back;
6506   } turn[] =
6507   {
6508     { 0,        0,              0        },
6509     { MV_DOWN,  MV_UP,          MV_RIGHT },
6510     { MV_UP,    MV_DOWN,        MV_LEFT  },
6511     { 0,        0,              0        },
6512     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6513     { 0,        0,              0        },
6514     { 0,        0,              0        },
6515     { 0,        0,              0        },
6516     { MV_RIGHT, MV_LEFT,        MV_UP    }
6517   };
6518
6519   int element = Feld[x][y];
6520   int move_pattern = element_info[element].move_pattern;
6521
6522   int old_move_dir = MovDir[x][y];
6523   int left_dir  = turn[old_move_dir].left;
6524   int right_dir = turn[old_move_dir].right;
6525   int back_dir  = turn[old_move_dir].back;
6526
6527   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6528   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6529   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6530   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6531
6532   int left_x  = x + left_dx,  left_y  = y + left_dy;
6533   int right_x = x + right_dx, right_y = y + right_dy;
6534   int move_x  = x + move_dx,  move_y  = y + move_dy;
6535
6536   int xx, yy;
6537
6538   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6539   {
6540     TestIfBadThingTouchesOtherBadThing(x, y);
6541
6542     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6543       MovDir[x][y] = right_dir;
6544     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6545       MovDir[x][y] = left_dir;
6546
6547     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6548       MovDelay[x][y] = 9;
6549     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6550       MovDelay[x][y] = 1;
6551   }
6552   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6553   {
6554     TestIfBadThingTouchesOtherBadThing(x, y);
6555
6556     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6557       MovDir[x][y] = left_dir;
6558     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6559       MovDir[x][y] = right_dir;
6560
6561     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6562       MovDelay[x][y] = 9;
6563     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6564       MovDelay[x][y] = 1;
6565   }
6566   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6567   {
6568     TestIfBadThingTouchesOtherBadThing(x, y);
6569
6570     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6571       MovDir[x][y] = left_dir;
6572     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6573       MovDir[x][y] = right_dir;
6574
6575     if (MovDir[x][y] != old_move_dir)
6576       MovDelay[x][y] = 9;
6577   }
6578   else if (element == EL_YAMYAM)
6579   {
6580     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6581     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6582
6583     if (can_turn_left && can_turn_right)
6584       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6585     else if (can_turn_left)
6586       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6587     else if (can_turn_right)
6588       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6589     else
6590       MovDir[x][y] = back_dir;
6591
6592     MovDelay[x][y] = 16 + 16 * RND(3);
6593   }
6594   else if (element == EL_DARK_YAMYAM)
6595   {
6596     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6597                                                          left_x, left_y);
6598     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6599                                                          right_x, right_y);
6600
6601     if (can_turn_left && can_turn_right)
6602       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6603     else if (can_turn_left)
6604       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6605     else if (can_turn_right)
6606       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6607     else
6608       MovDir[x][y] = back_dir;
6609
6610     MovDelay[x][y] = 16 + 16 * RND(3);
6611   }
6612   else if (element == EL_PACMAN)
6613   {
6614     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6615     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6616
6617     if (can_turn_left && can_turn_right)
6618       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6619     else if (can_turn_left)
6620       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6621     else if (can_turn_right)
6622       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6623     else
6624       MovDir[x][y] = back_dir;
6625
6626     MovDelay[x][y] = 6 + RND(40);
6627   }
6628   else if (element == EL_PIG)
6629   {
6630     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6631     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6632     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6633     boolean should_turn_left, should_turn_right, should_move_on;
6634     int rnd_value = 24;
6635     int rnd = RND(rnd_value);
6636
6637     should_turn_left = (can_turn_left &&
6638                         (!can_move_on ||
6639                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6640                                                    y + back_dy + left_dy)));
6641     should_turn_right = (can_turn_right &&
6642                          (!can_move_on ||
6643                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6644                                                     y + back_dy + right_dy)));
6645     should_move_on = (can_move_on &&
6646                       (!can_turn_left ||
6647                        !can_turn_right ||
6648                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6649                                                  y + move_dy + left_dy) ||
6650                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6651                                                  y + move_dy + right_dy)));
6652
6653     if (should_turn_left || should_turn_right || should_move_on)
6654     {
6655       if (should_turn_left && should_turn_right && should_move_on)
6656         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6657                         rnd < 2 * rnd_value / 3 ? right_dir :
6658                         old_move_dir);
6659       else if (should_turn_left && should_turn_right)
6660         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6661       else if (should_turn_left && should_move_on)
6662         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6663       else if (should_turn_right && should_move_on)
6664         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6665       else if (should_turn_left)
6666         MovDir[x][y] = left_dir;
6667       else if (should_turn_right)
6668         MovDir[x][y] = right_dir;
6669       else if (should_move_on)
6670         MovDir[x][y] = old_move_dir;
6671     }
6672     else if (can_move_on && rnd > rnd_value / 8)
6673       MovDir[x][y] = old_move_dir;
6674     else if (can_turn_left && can_turn_right)
6675       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6676     else if (can_turn_left && rnd > rnd_value / 8)
6677       MovDir[x][y] = left_dir;
6678     else if (can_turn_right && rnd > rnd_value/8)
6679       MovDir[x][y] = right_dir;
6680     else
6681       MovDir[x][y] = back_dir;
6682
6683     xx = x + move_xy[MovDir[x][y]].dx;
6684     yy = y + move_xy[MovDir[x][y]].dy;
6685
6686     if (!IN_LEV_FIELD(xx, yy) ||
6687         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6688       MovDir[x][y] = old_move_dir;
6689
6690     MovDelay[x][y] = 0;
6691   }
6692   else if (element == EL_DRAGON)
6693   {
6694     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6695     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6696     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6697     int rnd_value = 24;
6698     int rnd = RND(rnd_value);
6699
6700     if (can_move_on && rnd > rnd_value / 8)
6701       MovDir[x][y] = old_move_dir;
6702     else if (can_turn_left && can_turn_right)
6703       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6704     else if (can_turn_left && rnd > rnd_value / 8)
6705       MovDir[x][y] = left_dir;
6706     else if (can_turn_right && rnd > rnd_value / 8)
6707       MovDir[x][y] = right_dir;
6708     else
6709       MovDir[x][y] = back_dir;
6710
6711     xx = x + move_xy[MovDir[x][y]].dx;
6712     yy = y + move_xy[MovDir[x][y]].dy;
6713
6714     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6715       MovDir[x][y] = old_move_dir;
6716
6717     MovDelay[x][y] = 0;
6718   }
6719   else if (element == EL_MOLE)
6720   {
6721     boolean can_move_on =
6722       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6723                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6724                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6725     if (!can_move_on)
6726     {
6727       boolean can_turn_left =
6728         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6729                               IS_AMOEBOID(Feld[left_x][left_y])));
6730
6731       boolean can_turn_right =
6732         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6733                               IS_AMOEBOID(Feld[right_x][right_y])));
6734
6735       if (can_turn_left && can_turn_right)
6736         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6737       else if (can_turn_left)
6738         MovDir[x][y] = left_dir;
6739       else
6740         MovDir[x][y] = right_dir;
6741     }
6742
6743     if (MovDir[x][y] != old_move_dir)
6744       MovDelay[x][y] = 9;
6745   }
6746   else if (element == EL_BALLOON)
6747   {
6748     MovDir[x][y] = game.wind_direction;
6749     MovDelay[x][y] = 0;
6750   }
6751   else if (element == EL_SPRING)
6752   {
6753     if (MovDir[x][y] & MV_HORIZONTAL)
6754     {
6755       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6756           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6757       {
6758         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6759         ResetGfxAnimation(move_x, move_y);
6760         TEST_DrawLevelField(move_x, move_y);
6761
6762         MovDir[x][y] = back_dir;
6763       }
6764       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6765                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6766         MovDir[x][y] = MV_NONE;
6767     }
6768
6769     MovDelay[x][y] = 0;
6770   }
6771   else if (element == EL_ROBOT ||
6772            element == EL_SATELLITE ||
6773            element == EL_PENGUIN ||
6774            element == EL_EMC_ANDROID)
6775   {
6776     int attr_x = -1, attr_y = -1;
6777
6778     if (AllPlayersGone)
6779     {
6780       attr_x = ExitX;
6781       attr_y = ExitY;
6782     }
6783     else
6784     {
6785       int i;
6786
6787       for (i = 0; i < MAX_PLAYERS; i++)
6788       {
6789         struct PlayerInfo *player = &stored_player[i];
6790         int jx = player->jx, jy = player->jy;
6791
6792         if (!player->active)
6793           continue;
6794
6795         if (attr_x == -1 ||
6796             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6797         {
6798           attr_x = jx;
6799           attr_y = jy;
6800         }
6801       }
6802     }
6803
6804     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6805         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6806          game.engine_version < VERSION_IDENT(3,1,0,0)))
6807     {
6808       attr_x = ZX;
6809       attr_y = ZY;
6810     }
6811
6812     if (element == EL_PENGUIN)
6813     {
6814       int i;
6815       static int xy[4][2] =
6816       {
6817         { 0, -1 },
6818         { -1, 0 },
6819         { +1, 0 },
6820         { 0, +1 }
6821       };
6822
6823       for (i = 0; i < NUM_DIRECTIONS; i++)
6824       {
6825         int ex = x + xy[i][0];
6826         int ey = y + xy[i][1];
6827
6828         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6829                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6830                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6831                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6832         {
6833           attr_x = ex;
6834           attr_y = ey;
6835           break;
6836         }
6837       }
6838     }
6839
6840     MovDir[x][y] = MV_NONE;
6841     if (attr_x < x)
6842       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6843     else if (attr_x > x)
6844       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6845     if (attr_y < y)
6846       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6847     else if (attr_y > y)
6848       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6849
6850     if (element == EL_ROBOT)
6851     {
6852       int newx, newy;
6853
6854       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6855         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6856       Moving2Blocked(x, y, &newx, &newy);
6857
6858       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6859         MovDelay[x][y] = 8 + 8 * !RND(3);
6860       else
6861         MovDelay[x][y] = 16;
6862     }
6863     else if (element == EL_PENGUIN)
6864     {
6865       int newx, newy;
6866
6867       MovDelay[x][y] = 1;
6868
6869       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6870       {
6871         boolean first_horiz = RND(2);
6872         int new_move_dir = MovDir[x][y];
6873
6874         MovDir[x][y] =
6875           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6876         Moving2Blocked(x, y, &newx, &newy);
6877
6878         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6879           return;
6880
6881         MovDir[x][y] =
6882           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6883         Moving2Blocked(x, y, &newx, &newy);
6884
6885         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6886           return;
6887
6888         MovDir[x][y] = old_move_dir;
6889         return;
6890       }
6891     }
6892     else if (element == EL_SATELLITE)
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 (SATELLITE_CAN_ENTER_FIELD(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 (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6915           return;
6916
6917         MovDir[x][y] = old_move_dir;
6918         return;
6919       }
6920     }
6921     else if (element == EL_EMC_ANDROID)
6922     {
6923       static int check_pos[16] =
6924       {
6925         -1,             /*  0 => (invalid)          */
6926         7,              /*  1 => MV_LEFT            */
6927         3,              /*  2 => MV_RIGHT           */
6928         -1,             /*  3 => (invalid)          */
6929         1,              /*  4 =>            MV_UP   */
6930         0,              /*  5 => MV_LEFT  | MV_UP   */
6931         2,              /*  6 => MV_RIGHT | MV_UP   */
6932         -1,             /*  7 => (invalid)          */
6933         5,              /*  8 =>            MV_DOWN */
6934         6,              /*  9 => MV_LEFT  | MV_DOWN */
6935         4,              /* 10 => MV_RIGHT | MV_DOWN */
6936         -1,             /* 11 => (invalid)          */
6937         -1,             /* 12 => (invalid)          */
6938         -1,             /* 13 => (invalid)          */
6939         -1,             /* 14 => (invalid)          */
6940         -1,             /* 15 => (invalid)          */
6941       };
6942       static struct
6943       {
6944         int dx, dy;
6945         int dir;
6946       } check_xy[8] =
6947       {
6948         { -1, -1,       MV_LEFT  | MV_UP   },
6949         {  0, -1,                  MV_UP   },
6950         { +1, -1,       MV_RIGHT | MV_UP   },
6951         { +1,  0,       MV_RIGHT           },
6952         { +1, +1,       MV_RIGHT | MV_DOWN },
6953         {  0, +1,                  MV_DOWN },
6954         { -1, +1,       MV_LEFT  | MV_DOWN },
6955         { -1,  0,       MV_LEFT            },
6956       };
6957       int start_pos, check_order;
6958       boolean can_clone = FALSE;
6959       int i;
6960
6961       /* check if there is any free field around current position */
6962       for (i = 0; i < 8; i++)
6963       {
6964         int newx = x + check_xy[i].dx;
6965         int newy = y + check_xy[i].dy;
6966
6967         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6968         {
6969           can_clone = TRUE;
6970
6971           break;
6972         }
6973       }
6974
6975       if (can_clone)            /* randomly find an element to clone */
6976       {
6977         can_clone = FALSE;
6978
6979         start_pos = check_pos[RND(8)];
6980         check_order = (RND(2) ? -1 : +1);
6981
6982         for (i = 0; i < 8; i++)
6983         {
6984           int pos_raw = start_pos + i * check_order;
6985           int pos = (pos_raw + 8) % 8;
6986           int newx = x + check_xy[pos].dx;
6987           int newy = y + check_xy[pos].dy;
6988
6989           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6990           {
6991             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6992             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6993
6994             Store[x][y] = Feld[newx][newy];
6995
6996             can_clone = TRUE;
6997
6998             break;
6999           }
7000         }
7001       }
7002
7003       if (can_clone)            /* randomly find a direction to move */
7004       {
7005         can_clone = FALSE;
7006
7007         start_pos = check_pos[RND(8)];
7008         check_order = (RND(2) ? -1 : +1);
7009
7010         for (i = 0; i < 8; i++)
7011         {
7012           int pos_raw = start_pos + i * check_order;
7013           int pos = (pos_raw + 8) % 8;
7014           int newx = x + check_xy[pos].dx;
7015           int newy = y + check_xy[pos].dy;
7016           int new_move_dir = check_xy[pos].dir;
7017
7018           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7019           {
7020             MovDir[x][y] = new_move_dir;
7021             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7022
7023             can_clone = TRUE;
7024
7025             break;
7026           }
7027         }
7028       }
7029
7030       if (can_clone)            /* cloning and moving successful */
7031         return;
7032
7033       /* cannot clone -- try to move towards player */
7034
7035       start_pos = check_pos[MovDir[x][y] & 0x0f];
7036       check_order = (RND(2) ? -1 : +1);
7037
7038       for (i = 0; i < 3; i++)
7039       {
7040         /* first check start_pos, then previous/next or (next/previous) pos */
7041         int pos_raw = start_pos + (i < 2 ? i : -1) * 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 (IS_PLAYER(newx, newy))
7048           break;
7049
7050         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7051         {
7052           MovDir[x][y] = new_move_dir;
7053           MovDelay[x][y] = level.android_move_time * 8 + 1;
7054
7055           break;
7056         }
7057       }
7058     }
7059   }
7060   else if (move_pattern == MV_TURNING_LEFT ||
7061            move_pattern == MV_TURNING_RIGHT ||
7062            move_pattern == MV_TURNING_LEFT_RIGHT ||
7063            move_pattern == MV_TURNING_RIGHT_LEFT ||
7064            move_pattern == MV_TURNING_RANDOM ||
7065            move_pattern == MV_ALL_DIRECTIONS)
7066   {
7067     boolean can_turn_left =
7068       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7069     boolean can_turn_right =
7070       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7071
7072     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7073       return;
7074
7075     if (move_pattern == MV_TURNING_LEFT)
7076       MovDir[x][y] = left_dir;
7077     else if (move_pattern == MV_TURNING_RIGHT)
7078       MovDir[x][y] = right_dir;
7079     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7080       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7081     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7082       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7083     else if (move_pattern == MV_TURNING_RANDOM)
7084       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7085                       can_turn_right && !can_turn_left ? right_dir :
7086                       RND(2) ? left_dir : right_dir);
7087     else if (can_turn_left && can_turn_right)
7088       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7089     else if (can_turn_left)
7090       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7091     else if (can_turn_right)
7092       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7093     else
7094       MovDir[x][y] = back_dir;
7095
7096     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7097   }
7098   else if (move_pattern == MV_HORIZONTAL ||
7099            move_pattern == MV_VERTICAL)
7100   {
7101     if (move_pattern & old_move_dir)
7102       MovDir[x][y] = back_dir;
7103     else if (move_pattern == MV_HORIZONTAL)
7104       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7105     else if (move_pattern == MV_VERTICAL)
7106       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7107
7108     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7109   }
7110   else if (move_pattern & MV_ANY_DIRECTION)
7111   {
7112     MovDir[x][y] = move_pattern;
7113     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7114   }
7115   else if (move_pattern & MV_WIND_DIRECTION)
7116   {
7117     MovDir[x][y] = game.wind_direction;
7118     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7119   }
7120   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7121   {
7122     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7123       MovDir[x][y] = left_dir;
7124     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7125       MovDir[x][y] = right_dir;
7126
7127     if (MovDir[x][y] != old_move_dir)
7128       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7129   }
7130   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7131   {
7132     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7133       MovDir[x][y] = right_dir;
7134     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7135       MovDir[x][y] = left_dir;
7136
7137     if (MovDir[x][y] != old_move_dir)
7138       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7139   }
7140   else if (move_pattern == MV_TOWARDS_PLAYER ||
7141            move_pattern == MV_AWAY_FROM_PLAYER)
7142   {
7143     int attr_x = -1, attr_y = -1;
7144     int newx, newy;
7145     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7146
7147     if (AllPlayersGone)
7148     {
7149       attr_x = ExitX;
7150       attr_y = ExitY;
7151     }
7152     else
7153     {
7154       int i;
7155
7156       for (i = 0; i < MAX_PLAYERS; i++)
7157       {
7158         struct PlayerInfo *player = &stored_player[i];
7159         int jx = player->jx, jy = player->jy;
7160
7161         if (!player->active)
7162           continue;
7163
7164         if (attr_x == -1 ||
7165             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7166         {
7167           attr_x = jx;
7168           attr_y = jy;
7169         }
7170       }
7171     }
7172
7173     MovDir[x][y] = MV_NONE;
7174     if (attr_x < x)
7175       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7176     else if (attr_x > x)
7177       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7178     if (attr_y < y)
7179       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7180     else if (attr_y > y)
7181       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7182
7183     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7184
7185     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7186     {
7187       boolean first_horiz = RND(2);
7188       int new_move_dir = MovDir[x][y];
7189
7190       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7191       {
7192         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7193         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7194
7195         return;
7196       }
7197
7198       MovDir[x][y] =
7199         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7200       Moving2Blocked(x, y, &newx, &newy);
7201
7202       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7203         return;
7204
7205       MovDir[x][y] =
7206         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7207       Moving2Blocked(x, y, &newx, &newy);
7208
7209       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7210         return;
7211
7212       MovDir[x][y] = old_move_dir;
7213     }
7214   }
7215   else if (move_pattern == MV_WHEN_PUSHED ||
7216            move_pattern == MV_WHEN_DROPPED)
7217   {
7218     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7219       MovDir[x][y] = MV_NONE;
7220
7221     MovDelay[x][y] = 0;
7222   }
7223   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7224   {
7225     static int test_xy[7][2] =
7226     {
7227       { 0, -1 },
7228       { -1, 0 },
7229       { +1, 0 },
7230       { 0, +1 },
7231       { 0, -1 },
7232       { -1, 0 },
7233       { +1, 0 },
7234     };
7235     static int test_dir[7] =
7236     {
7237       MV_UP,
7238       MV_LEFT,
7239       MV_RIGHT,
7240       MV_DOWN,
7241       MV_UP,
7242       MV_LEFT,
7243       MV_RIGHT,
7244     };
7245     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7246     int move_preference = -1000000;     /* start with very low preference */
7247     int new_move_dir = MV_NONE;
7248     int start_test = RND(4);
7249     int i;
7250
7251     for (i = 0; i < NUM_DIRECTIONS; i++)
7252     {
7253       int move_dir = test_dir[start_test + i];
7254       int move_dir_preference;
7255
7256       xx = x + test_xy[start_test + i][0];
7257       yy = y + test_xy[start_test + i][1];
7258
7259       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7260           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7261       {
7262         new_move_dir = move_dir;
7263
7264         break;
7265       }
7266
7267       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7268         continue;
7269
7270       move_dir_preference = -1 * RunnerVisit[xx][yy];
7271       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7272         move_dir_preference = PlayerVisit[xx][yy];
7273
7274       if (move_dir_preference > move_preference)
7275       {
7276         /* prefer field that has not been visited for the longest time */
7277         move_preference = move_dir_preference;
7278         new_move_dir = move_dir;
7279       }
7280       else if (move_dir_preference == move_preference &&
7281                move_dir == old_move_dir)
7282       {
7283         /* prefer last direction when all directions are preferred equally */
7284         move_preference = move_dir_preference;
7285         new_move_dir = move_dir;
7286       }
7287     }
7288
7289     MovDir[x][y] = new_move_dir;
7290     if (old_move_dir != new_move_dir)
7291       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7292   }
7293 }
7294
7295 static void TurnRound(int x, int y)
7296 {
7297   int direction = MovDir[x][y];
7298
7299   TurnRoundExt(x, y);
7300
7301   GfxDir[x][y] = MovDir[x][y];
7302
7303   if (direction != MovDir[x][y])
7304     GfxFrame[x][y] = 0;
7305
7306   if (MovDelay[x][y])
7307     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7308
7309   ResetGfxFrame(x, y);
7310 }
7311
7312 static boolean JustBeingPushed(int x, int y)
7313 {
7314   int i;
7315
7316   for (i = 0; i < MAX_PLAYERS; i++)
7317   {
7318     struct PlayerInfo *player = &stored_player[i];
7319
7320     if (player->active && player->is_pushing && player->MovPos)
7321     {
7322       int next_jx = player->jx + (player->jx - player->last_jx);
7323       int next_jy = player->jy + (player->jy - player->last_jy);
7324
7325       if (x == next_jx && y == next_jy)
7326         return TRUE;
7327     }
7328   }
7329
7330   return FALSE;
7331 }
7332
7333 void StartMoving(int x, int y)
7334 {
7335   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7336   int element = Feld[x][y];
7337
7338   if (Stop[x][y])
7339     return;
7340
7341   if (MovDelay[x][y] == 0)
7342     GfxAction[x][y] = ACTION_DEFAULT;
7343
7344   if (CAN_FALL(element) && y < lev_fieldy - 1)
7345   {
7346     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7347         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7348       if (JustBeingPushed(x, y))
7349         return;
7350
7351     if (element == EL_QUICKSAND_FULL)
7352     {
7353       if (IS_FREE(x, y + 1))
7354       {
7355         InitMovingField(x, y, MV_DOWN);
7356         started_moving = TRUE;
7357
7358         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7359 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7360         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7361           Store[x][y] = EL_ROCK;
7362 #else
7363         Store[x][y] = EL_ROCK;
7364 #endif
7365
7366         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7367       }
7368       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7369       {
7370         if (!MovDelay[x][y])
7371         {
7372           MovDelay[x][y] = TILEY + 1;
7373
7374           ResetGfxAnimation(x, y);
7375           ResetGfxAnimation(x, y + 1);
7376         }
7377
7378         if (MovDelay[x][y])
7379         {
7380           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7381           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7382
7383           MovDelay[x][y]--;
7384           if (MovDelay[x][y])
7385             return;
7386         }
7387
7388         Feld[x][y] = EL_QUICKSAND_EMPTY;
7389         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7390         Store[x][y + 1] = Store[x][y];
7391         Store[x][y] = 0;
7392
7393         PlayLevelSoundAction(x, y, ACTION_FILLING);
7394       }
7395       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7396       {
7397         if (!MovDelay[x][y])
7398         {
7399           MovDelay[x][y] = TILEY + 1;
7400
7401           ResetGfxAnimation(x, y);
7402           ResetGfxAnimation(x, y + 1);
7403         }
7404
7405         if (MovDelay[x][y])
7406         {
7407           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7408           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7409
7410           MovDelay[x][y]--;
7411           if (MovDelay[x][y])
7412             return;
7413         }
7414
7415         Feld[x][y] = EL_QUICKSAND_EMPTY;
7416         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7417         Store[x][y + 1] = Store[x][y];
7418         Store[x][y] = 0;
7419
7420         PlayLevelSoundAction(x, y, ACTION_FILLING);
7421       }
7422     }
7423     else if (element == EL_QUICKSAND_FAST_FULL)
7424     {
7425       if (IS_FREE(x, y + 1))
7426       {
7427         InitMovingField(x, y, MV_DOWN);
7428         started_moving = TRUE;
7429
7430         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7431 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7432         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7433           Store[x][y] = EL_ROCK;
7434 #else
7435         Store[x][y] = EL_ROCK;
7436 #endif
7437
7438         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7439       }
7440       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7441       {
7442         if (!MovDelay[x][y])
7443         {
7444           MovDelay[x][y] = TILEY + 1;
7445
7446           ResetGfxAnimation(x, y);
7447           ResetGfxAnimation(x, y + 1);
7448         }
7449
7450         if (MovDelay[x][y])
7451         {
7452           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7453           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7454
7455           MovDelay[x][y]--;
7456           if (MovDelay[x][y])
7457             return;
7458         }
7459
7460         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7461         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7462         Store[x][y + 1] = Store[x][y];
7463         Store[x][y] = 0;
7464
7465         PlayLevelSoundAction(x, y, ACTION_FILLING);
7466       }
7467       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7468       {
7469         if (!MovDelay[x][y])
7470         {
7471           MovDelay[x][y] = TILEY + 1;
7472
7473           ResetGfxAnimation(x, y);
7474           ResetGfxAnimation(x, y + 1);
7475         }
7476
7477         if (MovDelay[x][y])
7478         {
7479           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7480           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7481
7482           MovDelay[x][y]--;
7483           if (MovDelay[x][y])
7484             return;
7485         }
7486
7487         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7488         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7489         Store[x][y + 1] = Store[x][y];
7490         Store[x][y] = 0;
7491
7492         PlayLevelSoundAction(x, y, ACTION_FILLING);
7493       }
7494     }
7495     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7496              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7497     {
7498       InitMovingField(x, y, MV_DOWN);
7499       started_moving = TRUE;
7500
7501       Feld[x][y] = EL_QUICKSAND_FILLING;
7502       Store[x][y] = element;
7503
7504       PlayLevelSoundAction(x, y, ACTION_FILLING);
7505     }
7506     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7507              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7508     {
7509       InitMovingField(x, y, MV_DOWN);
7510       started_moving = TRUE;
7511
7512       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7513       Store[x][y] = element;
7514
7515       PlayLevelSoundAction(x, y, ACTION_FILLING);
7516     }
7517     else if (element == EL_MAGIC_WALL_FULL)
7518     {
7519       if (IS_FREE(x, y + 1))
7520       {
7521         InitMovingField(x, y, MV_DOWN);
7522         started_moving = TRUE;
7523
7524         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7525         Store[x][y] = EL_CHANGED(Store[x][y]);
7526       }
7527       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7528       {
7529         if (!MovDelay[x][y])
7530           MovDelay[x][y] = TILEY / 4 + 1;
7531
7532         if (MovDelay[x][y])
7533         {
7534           MovDelay[x][y]--;
7535           if (MovDelay[x][y])
7536             return;
7537         }
7538
7539         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7540         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7541         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7542         Store[x][y] = 0;
7543       }
7544     }
7545     else if (element == EL_BD_MAGIC_WALL_FULL)
7546     {
7547       if (IS_FREE(x, y + 1))
7548       {
7549         InitMovingField(x, y, MV_DOWN);
7550         started_moving = TRUE;
7551
7552         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7553         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7554       }
7555       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7556       {
7557         if (!MovDelay[x][y])
7558           MovDelay[x][y] = TILEY / 4 + 1;
7559
7560         if (MovDelay[x][y])
7561         {
7562           MovDelay[x][y]--;
7563           if (MovDelay[x][y])
7564             return;
7565         }
7566
7567         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7568         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7569         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7570         Store[x][y] = 0;
7571       }
7572     }
7573     else if (element == EL_DC_MAGIC_WALL_FULL)
7574     {
7575       if (IS_FREE(x, y + 1))
7576       {
7577         InitMovingField(x, y, MV_DOWN);
7578         started_moving = TRUE;
7579
7580         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7581         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7582       }
7583       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7584       {
7585         if (!MovDelay[x][y])
7586           MovDelay[x][y] = TILEY / 4 + 1;
7587
7588         if (MovDelay[x][y])
7589         {
7590           MovDelay[x][y]--;
7591           if (MovDelay[x][y])
7592             return;
7593         }
7594
7595         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7596         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7597         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7598         Store[x][y] = 0;
7599       }
7600     }
7601     else if ((CAN_PASS_MAGIC_WALL(element) &&
7602               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7603                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7604              (CAN_PASS_DC_MAGIC_WALL(element) &&
7605               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7606
7607     {
7608       InitMovingField(x, y, MV_DOWN);
7609       started_moving = TRUE;
7610
7611       Feld[x][y] =
7612         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7613          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7614          EL_DC_MAGIC_WALL_FILLING);
7615       Store[x][y] = element;
7616     }
7617     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7618     {
7619       SplashAcid(x, y + 1);
7620
7621       InitMovingField(x, y, MV_DOWN);
7622       started_moving = TRUE;
7623
7624       Store[x][y] = EL_ACID;
7625     }
7626     else if (
7627              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7628               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7629              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7630               CAN_FALL(element) && WasJustFalling[x][y] &&
7631               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7632
7633              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7634               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7635               (Feld[x][y + 1] == EL_BLOCKED)))
7636     {
7637       /* this is needed for a special case not covered by calling "Impact()"
7638          from "ContinueMoving()": if an element moves to a tile directly below
7639          another element which was just falling on that tile (which was empty
7640          in the previous frame), the falling element above would just stop
7641          instead of smashing the element below (in previous version, the above
7642          element was just checked for "moving" instead of "falling", resulting
7643          in incorrect smashes caused by horizontal movement of the above
7644          element; also, the case of the player being the element to smash was
7645          simply not covered here... :-/ ) */
7646
7647       CheckCollision[x][y] = 0;
7648       CheckImpact[x][y] = 0;
7649
7650       Impact(x, y);
7651     }
7652     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7653     {
7654       if (MovDir[x][y] == MV_NONE)
7655       {
7656         InitMovingField(x, y, MV_DOWN);
7657         started_moving = TRUE;
7658       }
7659     }
7660     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7661     {
7662       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7663         MovDir[x][y] = MV_DOWN;
7664
7665       InitMovingField(x, y, MV_DOWN);
7666       started_moving = TRUE;
7667     }
7668     else if (element == EL_AMOEBA_DROP)
7669     {
7670       Feld[x][y] = EL_AMOEBA_GROWING;
7671       Store[x][y] = EL_AMOEBA_WET;
7672     }
7673     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7674               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7675              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7676              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7677     {
7678       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7679                                 (IS_FREE(x - 1, y + 1) ||
7680                                  Feld[x - 1][y + 1] == EL_ACID));
7681       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7682                                 (IS_FREE(x + 1, y + 1) ||
7683                                  Feld[x + 1][y + 1] == EL_ACID));
7684       boolean can_fall_any  = (can_fall_left || can_fall_right);
7685       boolean can_fall_both = (can_fall_left && can_fall_right);
7686       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7687
7688       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7689       {
7690         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7691           can_fall_right = FALSE;
7692         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7693           can_fall_left = FALSE;
7694         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7695           can_fall_right = FALSE;
7696         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7697           can_fall_left = FALSE;
7698
7699         can_fall_any  = (can_fall_left || can_fall_right);
7700         can_fall_both = FALSE;
7701       }
7702
7703       if (can_fall_both)
7704       {
7705         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7706           can_fall_right = FALSE;       /* slip down on left side */
7707         else
7708           can_fall_left = !(can_fall_right = RND(2));
7709
7710         can_fall_both = FALSE;
7711       }
7712
7713       if (can_fall_any)
7714       {
7715         /* if not determined otherwise, prefer left side for slipping down */
7716         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7717         started_moving = TRUE;
7718       }
7719     }
7720     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7721     {
7722       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7723       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7724       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7725       int belt_dir = game.belt_dir[belt_nr];
7726
7727       if ((belt_dir == MV_LEFT  && left_is_free) ||
7728           (belt_dir == MV_RIGHT && right_is_free))
7729       {
7730         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7731
7732         InitMovingField(x, y, belt_dir);
7733         started_moving = TRUE;
7734
7735         Pushed[x][y] = TRUE;
7736         Pushed[nextx][y] = TRUE;
7737
7738         GfxAction[x][y] = ACTION_DEFAULT;
7739       }
7740       else
7741       {
7742         MovDir[x][y] = 0;       /* if element was moving, stop it */
7743       }
7744     }
7745   }
7746
7747   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7748   if (CAN_MOVE(element) && !started_moving)
7749   {
7750     int move_pattern = element_info[element].move_pattern;
7751     int newx, newy;
7752
7753     Moving2Blocked(x, y, &newx, &newy);
7754
7755     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7756       return;
7757
7758     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7759         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7760     {
7761       WasJustMoving[x][y] = 0;
7762       CheckCollision[x][y] = 0;
7763
7764       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7765
7766       if (Feld[x][y] != element)        /* element has changed */
7767         return;
7768     }
7769
7770     if (!MovDelay[x][y])        /* start new movement phase */
7771     {
7772       /* all objects that can change their move direction after each step
7773          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7774
7775       if (element != EL_YAMYAM &&
7776           element != EL_DARK_YAMYAM &&
7777           element != EL_PACMAN &&
7778           !(move_pattern & MV_ANY_DIRECTION) &&
7779           move_pattern != MV_TURNING_LEFT &&
7780           move_pattern != MV_TURNING_RIGHT &&
7781           move_pattern != MV_TURNING_LEFT_RIGHT &&
7782           move_pattern != MV_TURNING_RIGHT_LEFT &&
7783           move_pattern != MV_TURNING_RANDOM)
7784       {
7785         TurnRound(x, y);
7786
7787         if (MovDelay[x][y] && (element == EL_BUG ||
7788                                element == EL_SPACESHIP ||
7789                                element == EL_SP_SNIKSNAK ||
7790                                element == EL_SP_ELECTRON ||
7791                                element == EL_MOLE))
7792           TEST_DrawLevelField(x, y);
7793       }
7794     }
7795
7796     if (MovDelay[x][y])         /* wait some time before next movement */
7797     {
7798       MovDelay[x][y]--;
7799
7800       if (element == EL_ROBOT ||
7801           element == EL_YAMYAM ||
7802           element == EL_DARK_YAMYAM)
7803       {
7804         DrawLevelElementAnimationIfNeeded(x, y, element);
7805         PlayLevelSoundAction(x, y, ACTION_WAITING);
7806       }
7807       else if (element == EL_SP_ELECTRON)
7808         DrawLevelElementAnimationIfNeeded(x, y, element);
7809       else if (element == EL_DRAGON)
7810       {
7811         int i;
7812         int dir = MovDir[x][y];
7813         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7814         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7815         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7816                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7817                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7818                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7819         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7820
7821         GfxAction[x][y] = ACTION_ATTACKING;
7822
7823         if (IS_PLAYER(x, y))
7824           DrawPlayerField(x, y);
7825         else
7826           TEST_DrawLevelField(x, y);
7827
7828         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7829
7830         for (i = 1; i <= 3; i++)
7831         {
7832           int xx = x + i * dx;
7833           int yy = y + i * dy;
7834           int sx = SCREENX(xx);
7835           int sy = SCREENY(yy);
7836           int flame_graphic = graphic + (i - 1);
7837
7838           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7839             break;
7840
7841           if (MovDelay[x][y])
7842           {
7843             int flamed = MovingOrBlocked2Element(xx, yy);
7844
7845             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7846               Bang(xx, yy);
7847             else
7848               RemoveMovingField(xx, yy);
7849
7850             ChangeDelay[xx][yy] = 0;
7851
7852             Feld[xx][yy] = EL_FLAMES;
7853
7854             if (IN_SCR_FIELD(sx, sy))
7855             {
7856               TEST_DrawLevelFieldCrumbled(xx, yy);
7857               DrawGraphic(sx, sy, flame_graphic, frame);
7858             }
7859           }
7860           else
7861           {
7862             if (Feld[xx][yy] == EL_FLAMES)
7863               Feld[xx][yy] = EL_EMPTY;
7864             TEST_DrawLevelField(xx, yy);
7865           }
7866         }
7867       }
7868
7869       if (MovDelay[x][y])       /* element still has to wait some time */
7870       {
7871         PlayLevelSoundAction(x, y, ACTION_WAITING);
7872
7873         return;
7874       }
7875     }
7876
7877     /* now make next step */
7878
7879     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7880
7881     if (DONT_COLLIDE_WITH(element) &&
7882         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7883         !PLAYER_ENEMY_PROTECTED(newx, newy))
7884     {
7885       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7886
7887       return;
7888     }
7889
7890     else if (CAN_MOVE_INTO_ACID(element) &&
7891              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7892              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7893              (MovDir[x][y] == MV_DOWN ||
7894               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7895     {
7896       SplashAcid(newx, newy);
7897       Store[x][y] = EL_ACID;
7898     }
7899     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7900     {
7901       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7902           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7903           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7904           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7905       {
7906         RemoveField(x, y);
7907         TEST_DrawLevelField(x, y);
7908
7909         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7910         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7911           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7912
7913         local_player->friends_still_needed--;
7914         if (!local_player->friends_still_needed &&
7915             !local_player->GameOver && AllPlayersGone)
7916           PlayerWins(local_player);
7917
7918         return;
7919       }
7920       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7921       {
7922         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7923           TEST_DrawLevelField(newx, newy);
7924         else
7925           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7926       }
7927       else if (!IS_FREE(newx, newy))
7928       {
7929         GfxAction[x][y] = ACTION_WAITING;
7930
7931         if (IS_PLAYER(x, y))
7932           DrawPlayerField(x, y);
7933         else
7934           TEST_DrawLevelField(x, y);
7935
7936         return;
7937       }
7938     }
7939     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7940     {
7941       if (IS_FOOD_PIG(Feld[newx][newy]))
7942       {
7943         if (IS_MOVING(newx, newy))
7944           RemoveMovingField(newx, newy);
7945         else
7946         {
7947           Feld[newx][newy] = EL_EMPTY;
7948           TEST_DrawLevelField(newx, newy);
7949         }
7950
7951         PlayLevelSound(x, y, SND_PIG_DIGGING);
7952       }
7953       else if (!IS_FREE(newx, newy))
7954       {
7955         if (IS_PLAYER(x, y))
7956           DrawPlayerField(x, y);
7957         else
7958           TEST_DrawLevelField(x, y);
7959
7960         return;
7961       }
7962     }
7963     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7964     {
7965       if (Store[x][y] != EL_EMPTY)
7966       {
7967         boolean can_clone = FALSE;
7968         int xx, yy;
7969
7970         /* check if element to clone is still there */
7971         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7972         {
7973           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7974           {
7975             can_clone = TRUE;
7976
7977             break;
7978           }
7979         }
7980
7981         /* cannot clone or target field not free anymore -- do not clone */
7982         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7983           Store[x][y] = EL_EMPTY;
7984       }
7985
7986       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7987       {
7988         if (IS_MV_DIAGONAL(MovDir[x][y]))
7989         {
7990           int diagonal_move_dir = MovDir[x][y];
7991           int stored = Store[x][y];
7992           int change_delay = 8;
7993           int graphic;
7994
7995           /* android is moving diagonally */
7996
7997           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7998
7999           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8000           GfxElement[x][y] = EL_EMC_ANDROID;
8001           GfxAction[x][y] = ACTION_SHRINKING;
8002           GfxDir[x][y] = diagonal_move_dir;
8003           ChangeDelay[x][y] = change_delay;
8004
8005           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8006                                    GfxDir[x][y]);
8007
8008           DrawLevelGraphicAnimation(x, y, graphic);
8009           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8010
8011           if (Feld[newx][newy] == EL_ACID)
8012           {
8013             SplashAcid(newx, newy);
8014
8015             return;
8016           }
8017
8018           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8019
8020           Store[newx][newy] = EL_EMC_ANDROID;
8021           GfxElement[newx][newy] = EL_EMC_ANDROID;
8022           GfxAction[newx][newy] = ACTION_GROWING;
8023           GfxDir[newx][newy] = diagonal_move_dir;
8024           ChangeDelay[newx][newy] = change_delay;
8025
8026           graphic = el_act_dir2img(GfxElement[newx][newy],
8027                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8028
8029           DrawLevelGraphicAnimation(newx, newy, graphic);
8030           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8031
8032           return;
8033         }
8034         else
8035         {
8036           Feld[newx][newy] = EL_EMPTY;
8037           TEST_DrawLevelField(newx, newy);
8038
8039           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8040         }
8041       }
8042       else if (!IS_FREE(newx, newy))
8043       {
8044         return;
8045       }
8046     }
8047     else if (IS_CUSTOM_ELEMENT(element) &&
8048              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8049     {
8050       if (!DigFieldByCE(newx, newy, element))
8051         return;
8052
8053       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8054       {
8055         RunnerVisit[x][y] = FrameCounter;
8056         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8057       }
8058     }
8059     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8060     {
8061       if (!IS_FREE(newx, newy))
8062       {
8063         if (IS_PLAYER(x, y))
8064           DrawPlayerField(x, y);
8065         else
8066           TEST_DrawLevelField(x, y);
8067
8068         return;
8069       }
8070       else
8071       {
8072         boolean wanna_flame = !RND(10);
8073         int dx = newx - x, dy = newy - y;
8074         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8075         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8076         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8077                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8078         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8079                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8080
8081         if ((wanna_flame ||
8082              IS_CLASSIC_ENEMY(element1) ||
8083              IS_CLASSIC_ENEMY(element2)) &&
8084             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8085             element1 != EL_FLAMES && element2 != EL_FLAMES)
8086         {
8087           ResetGfxAnimation(x, y);
8088           GfxAction[x][y] = ACTION_ATTACKING;
8089
8090           if (IS_PLAYER(x, y))
8091             DrawPlayerField(x, y);
8092           else
8093             TEST_DrawLevelField(x, y);
8094
8095           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8096
8097           MovDelay[x][y] = 50;
8098
8099           Feld[newx][newy] = EL_FLAMES;
8100           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8101             Feld[newx1][newy1] = EL_FLAMES;
8102           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8103             Feld[newx2][newy2] = EL_FLAMES;
8104
8105           return;
8106         }
8107       }
8108     }
8109     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8110              Feld[newx][newy] == EL_DIAMOND)
8111     {
8112       if (IS_MOVING(newx, newy))
8113         RemoveMovingField(newx, newy);
8114       else
8115       {
8116         Feld[newx][newy] = EL_EMPTY;
8117         TEST_DrawLevelField(newx, newy);
8118       }
8119
8120       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8121     }
8122     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8123              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8124     {
8125       if (AmoebaNr[newx][newy])
8126       {
8127         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8128         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8129             Feld[newx][newy] == EL_BD_AMOEBA)
8130           AmoebaCnt[AmoebaNr[newx][newy]]--;
8131       }
8132
8133       if (IS_MOVING(newx, newy))
8134       {
8135         RemoveMovingField(newx, newy);
8136       }
8137       else
8138       {
8139         Feld[newx][newy] = EL_EMPTY;
8140         TEST_DrawLevelField(newx, newy);
8141       }
8142
8143       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8144     }
8145     else if ((element == EL_PACMAN || element == EL_MOLE)
8146              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8147     {
8148       if (AmoebaNr[newx][newy])
8149       {
8150         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8151         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8152             Feld[newx][newy] == EL_BD_AMOEBA)
8153           AmoebaCnt[AmoebaNr[newx][newy]]--;
8154       }
8155
8156       if (element == EL_MOLE)
8157       {
8158         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8159         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8160
8161         ResetGfxAnimation(x, y);
8162         GfxAction[x][y] = ACTION_DIGGING;
8163         TEST_DrawLevelField(x, y);
8164
8165         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8166
8167         return;                         /* wait for shrinking amoeba */
8168       }
8169       else      /* element == EL_PACMAN */
8170       {
8171         Feld[newx][newy] = EL_EMPTY;
8172         TEST_DrawLevelField(newx, newy);
8173         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8174       }
8175     }
8176     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8177              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8178               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8179     {
8180       /* wait for shrinking amoeba to completely disappear */
8181       return;
8182     }
8183     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8184     {
8185       /* object was running against a wall */
8186
8187       TurnRound(x, y);
8188
8189       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8190         DrawLevelElementAnimation(x, y, element);
8191
8192       if (DONT_TOUCH(element))
8193         TestIfBadThingTouchesPlayer(x, y);
8194
8195       return;
8196     }
8197
8198     InitMovingField(x, y, MovDir[x][y]);
8199
8200     PlayLevelSoundAction(x, y, ACTION_MOVING);
8201   }
8202
8203   if (MovDir[x][y])
8204     ContinueMoving(x, y);
8205 }
8206
8207 void ContinueMoving(int x, int y)
8208 {
8209   int element = Feld[x][y];
8210   struct ElementInfo *ei = &element_info[element];
8211   int direction = MovDir[x][y];
8212   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8213   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8214   int newx = x + dx, newy = y + dy;
8215   int stored = Store[x][y];
8216   int stored_new = Store[newx][newy];
8217   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8218   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8219   boolean last_line = (newy == lev_fieldy - 1);
8220
8221   MovPos[x][y] += getElementMoveStepsize(x, y);
8222
8223   if (pushed_by_player) /* special case: moving object pushed by player */
8224     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8225
8226   if (ABS(MovPos[x][y]) < TILEX)
8227   {
8228     TEST_DrawLevelField(x, y);
8229
8230     return;     /* element is still moving */
8231   }
8232
8233   /* element reached destination field */
8234
8235   Feld[x][y] = EL_EMPTY;
8236   Feld[newx][newy] = element;
8237   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8238
8239   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8240   {
8241     element = Feld[newx][newy] = EL_ACID;
8242   }
8243   else if (element == EL_MOLE)
8244   {
8245     Feld[x][y] = EL_SAND;
8246
8247     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8248   }
8249   else if (element == EL_QUICKSAND_FILLING)
8250   {
8251     element = Feld[newx][newy] = get_next_element(element);
8252     Store[newx][newy] = Store[x][y];
8253   }
8254   else if (element == EL_QUICKSAND_EMPTYING)
8255   {
8256     Feld[x][y] = get_next_element(element);
8257     element = Feld[newx][newy] = Store[x][y];
8258   }
8259   else if (element == EL_QUICKSAND_FAST_FILLING)
8260   {
8261     element = Feld[newx][newy] = get_next_element(element);
8262     Store[newx][newy] = Store[x][y];
8263   }
8264   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8265   {
8266     Feld[x][y] = get_next_element(element);
8267     element = Feld[newx][newy] = Store[x][y];
8268   }
8269   else if (element == EL_MAGIC_WALL_FILLING)
8270   {
8271     element = Feld[newx][newy] = get_next_element(element);
8272     if (!game.magic_wall_active)
8273       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8274     Store[newx][newy] = Store[x][y];
8275   }
8276   else if (element == EL_MAGIC_WALL_EMPTYING)
8277   {
8278     Feld[x][y] = get_next_element(element);
8279     if (!game.magic_wall_active)
8280       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8281     element = Feld[newx][newy] = Store[x][y];
8282
8283     InitField(newx, newy, FALSE);
8284   }
8285   else if (element == EL_BD_MAGIC_WALL_FILLING)
8286   {
8287     element = Feld[newx][newy] = get_next_element(element);
8288     if (!game.magic_wall_active)
8289       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8290     Store[newx][newy] = Store[x][y];
8291   }
8292   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8293   {
8294     Feld[x][y] = get_next_element(element);
8295     if (!game.magic_wall_active)
8296       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8297     element = Feld[newx][newy] = Store[x][y];
8298
8299     InitField(newx, newy, FALSE);
8300   }
8301   else if (element == EL_DC_MAGIC_WALL_FILLING)
8302   {
8303     element = Feld[newx][newy] = get_next_element(element);
8304     if (!game.magic_wall_active)
8305       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8306     Store[newx][newy] = Store[x][y];
8307   }
8308   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8309   {
8310     Feld[x][y] = get_next_element(element);
8311     if (!game.magic_wall_active)
8312       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8313     element = Feld[newx][newy] = Store[x][y];
8314
8315     InitField(newx, newy, FALSE);
8316   }
8317   else if (element == EL_AMOEBA_DROPPING)
8318   {
8319     Feld[x][y] = get_next_element(element);
8320     element = Feld[newx][newy] = Store[x][y];
8321   }
8322   else if (element == EL_SOKOBAN_OBJECT)
8323   {
8324     if (Back[x][y])
8325       Feld[x][y] = Back[x][y];
8326
8327     if (Back[newx][newy])
8328       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8329
8330     Back[x][y] = Back[newx][newy] = 0;
8331   }
8332
8333   Store[x][y] = EL_EMPTY;
8334   MovPos[x][y] = 0;
8335   MovDir[x][y] = 0;
8336   MovDelay[x][y] = 0;
8337
8338   MovDelay[newx][newy] = 0;
8339
8340   if (CAN_CHANGE_OR_HAS_ACTION(element))
8341   {
8342     /* copy element change control values to new field */
8343     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8344     ChangePage[newx][newy]  = ChangePage[x][y];
8345     ChangeCount[newx][newy] = ChangeCount[x][y];
8346     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8347   }
8348
8349   CustomValue[newx][newy] = CustomValue[x][y];
8350
8351   ChangeDelay[x][y] = 0;
8352   ChangePage[x][y] = -1;
8353   ChangeCount[x][y] = 0;
8354   ChangeEvent[x][y] = -1;
8355
8356   CustomValue[x][y] = 0;
8357
8358   /* copy animation control values to new field */
8359   GfxFrame[newx][newy]  = GfxFrame[x][y];
8360   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8361   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8362   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8363
8364   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8365
8366   /* some elements can leave other elements behind after moving */
8367   if (ei->move_leave_element != EL_EMPTY &&
8368       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8369       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8370   {
8371     int move_leave_element = ei->move_leave_element;
8372
8373     /* this makes it possible to leave the removed element again */
8374     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8375       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8376
8377     Feld[x][y] = move_leave_element;
8378
8379     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8380       MovDir[x][y] = direction;
8381
8382     InitField(x, y, FALSE);
8383
8384     if (GFX_CRUMBLED(Feld[x][y]))
8385       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8386
8387     if (ELEM_IS_PLAYER(move_leave_element))
8388       RelocatePlayer(x, y, move_leave_element);
8389   }
8390
8391   /* do this after checking for left-behind element */
8392   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8393
8394   if (!CAN_MOVE(element) ||
8395       (CAN_FALL(element) && direction == MV_DOWN &&
8396        (element == EL_SPRING ||
8397         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8398         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8399     GfxDir[x][y] = MovDir[newx][newy] = 0;
8400
8401   TEST_DrawLevelField(x, y);
8402   TEST_DrawLevelField(newx, newy);
8403
8404   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8405
8406   /* prevent pushed element from moving on in pushed direction */
8407   if (pushed_by_player && CAN_MOVE(element) &&
8408       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8409       !(element_info[element].move_pattern & direction))
8410     TurnRound(newx, newy);
8411
8412   /* prevent elements on conveyor belt from moving on in last direction */
8413   if (pushed_by_conveyor && CAN_FALL(element) &&
8414       direction & MV_HORIZONTAL)
8415     MovDir[newx][newy] = 0;
8416
8417   if (!pushed_by_player)
8418   {
8419     int nextx = newx + dx, nexty = newy + dy;
8420     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8421
8422     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8423
8424     if (CAN_FALL(element) && direction == MV_DOWN)
8425       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8426
8427     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8428       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8429
8430     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8431       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8432   }
8433
8434   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8435   {
8436     TestIfBadThingTouchesPlayer(newx, newy);
8437     TestIfBadThingTouchesFriend(newx, newy);
8438
8439     if (!IS_CUSTOM_ELEMENT(element))
8440       TestIfBadThingTouchesOtherBadThing(newx, newy);
8441   }
8442   else if (element == EL_PENGUIN)
8443     TestIfFriendTouchesBadThing(newx, newy);
8444
8445   if (DONT_GET_HIT_BY(element))
8446   {
8447     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8448   }
8449
8450   /* give the player one last chance (one more frame) to move away */
8451   if (CAN_FALL(element) && direction == MV_DOWN &&
8452       (last_line || (!IS_FREE(x, newy + 1) &&
8453                      (!IS_PLAYER(x, newy + 1) ||
8454                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8455     Impact(x, newy);
8456
8457   if (pushed_by_player && !game.use_change_when_pushing_bug)
8458   {
8459     int push_side = MV_DIR_OPPOSITE(direction);
8460     struct PlayerInfo *player = PLAYERINFO(x, y);
8461
8462     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8463                                player->index_bit, push_side);
8464     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8465                                         player->index_bit, push_side);
8466   }
8467
8468   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8469     MovDelay[newx][newy] = 1;
8470
8471   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8472
8473   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8474   TestIfElementHitsCustomElement(newx, newy, direction);
8475   TestIfPlayerTouchesCustomElement(newx, newy);
8476   TestIfElementTouchesCustomElement(newx, newy);
8477
8478   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8479       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8480     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8481                              MV_DIR_OPPOSITE(direction));
8482 }
8483
8484 int AmoebeNachbarNr(int ax, int ay)
8485 {
8486   int i;
8487   int element = Feld[ax][ay];
8488   int group_nr = 0;
8489   static int xy[4][2] =
8490   {
8491     { 0, -1 },
8492     { -1, 0 },
8493     { +1, 0 },
8494     { 0, +1 }
8495   };
8496
8497   for (i = 0; i < NUM_DIRECTIONS; i++)
8498   {
8499     int x = ax + xy[i][0];
8500     int y = ay + xy[i][1];
8501
8502     if (!IN_LEV_FIELD(x, y))
8503       continue;
8504
8505     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8506       group_nr = AmoebaNr[x][y];
8507   }
8508
8509   return group_nr;
8510 }
8511
8512 void AmoebenVereinigen(int ax, int ay)
8513 {
8514   int i, x, y, xx, yy;
8515   int new_group_nr = AmoebaNr[ax][ay];
8516   static int xy[4][2] =
8517   {
8518     { 0, -1 },
8519     { -1, 0 },
8520     { +1, 0 },
8521     { 0, +1 }
8522   };
8523
8524   if (new_group_nr == 0)
8525     return;
8526
8527   for (i = 0; i < NUM_DIRECTIONS; i++)
8528   {
8529     x = ax + xy[i][0];
8530     y = ay + xy[i][1];
8531
8532     if (!IN_LEV_FIELD(x, y))
8533       continue;
8534
8535     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8536          Feld[x][y] == EL_BD_AMOEBA ||
8537          Feld[x][y] == EL_AMOEBA_DEAD) &&
8538         AmoebaNr[x][y] != new_group_nr)
8539     {
8540       int old_group_nr = AmoebaNr[x][y];
8541
8542       if (old_group_nr == 0)
8543         return;
8544
8545       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8546       AmoebaCnt[old_group_nr] = 0;
8547       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8548       AmoebaCnt2[old_group_nr] = 0;
8549
8550       SCAN_PLAYFIELD(xx, yy)
8551       {
8552         if (AmoebaNr[xx][yy] == old_group_nr)
8553           AmoebaNr[xx][yy] = new_group_nr;
8554       }
8555     }
8556   }
8557 }
8558
8559 void AmoebeUmwandeln(int ax, int ay)
8560 {
8561   int i, x, y;
8562
8563   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8564   {
8565     int group_nr = AmoebaNr[ax][ay];
8566
8567 #ifdef DEBUG
8568     if (group_nr == 0)
8569     {
8570       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8571       printf("AmoebeUmwandeln(): This should never happen!\n");
8572       return;
8573     }
8574 #endif
8575
8576     SCAN_PLAYFIELD(x, y)
8577     {
8578       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8579       {
8580         AmoebaNr[x][y] = 0;
8581         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8582       }
8583     }
8584
8585     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8586                             SND_AMOEBA_TURNING_TO_GEM :
8587                             SND_AMOEBA_TURNING_TO_ROCK));
8588     Bang(ax, ay);
8589   }
8590   else
8591   {
8592     static int xy[4][2] =
8593     {
8594       { 0, -1 },
8595       { -1, 0 },
8596       { +1, 0 },
8597       { 0, +1 }
8598     };
8599
8600     for (i = 0; i < NUM_DIRECTIONS; i++)
8601     {
8602       x = ax + xy[i][0];
8603       y = ay + xy[i][1];
8604
8605       if (!IN_LEV_FIELD(x, y))
8606         continue;
8607
8608       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8609       {
8610         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8611                               SND_AMOEBA_TURNING_TO_GEM :
8612                               SND_AMOEBA_TURNING_TO_ROCK));
8613         Bang(x, y);
8614       }
8615     }
8616   }
8617 }
8618
8619 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8620 {
8621   int x, y;
8622   int group_nr = AmoebaNr[ax][ay];
8623   boolean done = FALSE;
8624
8625 #ifdef DEBUG
8626   if (group_nr == 0)
8627   {
8628     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8629     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8630     return;
8631   }
8632 #endif
8633
8634   SCAN_PLAYFIELD(x, y)
8635   {
8636     if (AmoebaNr[x][y] == group_nr &&
8637         (Feld[x][y] == EL_AMOEBA_DEAD ||
8638          Feld[x][y] == EL_BD_AMOEBA ||
8639          Feld[x][y] == EL_AMOEBA_GROWING))
8640     {
8641       AmoebaNr[x][y] = 0;
8642       Feld[x][y] = new_element;
8643       InitField(x, y, FALSE);
8644       TEST_DrawLevelField(x, y);
8645       done = TRUE;
8646     }
8647   }
8648
8649   if (done)
8650     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8651                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8652                             SND_BD_AMOEBA_TURNING_TO_GEM));
8653 }
8654
8655 void AmoebeWaechst(int x, int y)
8656 {
8657   static unsigned int sound_delay = 0;
8658   static unsigned int sound_delay_value = 0;
8659
8660   if (!MovDelay[x][y])          /* start new growing cycle */
8661   {
8662     MovDelay[x][y] = 7;
8663
8664     if (DelayReached(&sound_delay, sound_delay_value))
8665     {
8666       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8667       sound_delay_value = 30;
8668     }
8669   }
8670
8671   if (MovDelay[x][y])           /* wait some time before growing bigger */
8672   {
8673     MovDelay[x][y]--;
8674     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8675     {
8676       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8677                                            6 - MovDelay[x][y]);
8678
8679       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8680     }
8681
8682     if (!MovDelay[x][y])
8683     {
8684       Feld[x][y] = Store[x][y];
8685       Store[x][y] = 0;
8686       TEST_DrawLevelField(x, y);
8687     }
8688   }
8689 }
8690
8691 void AmoebaDisappearing(int x, int y)
8692 {
8693   static unsigned int sound_delay = 0;
8694   static unsigned int sound_delay_value = 0;
8695
8696   if (!MovDelay[x][y])          /* start new shrinking cycle */
8697   {
8698     MovDelay[x][y] = 7;
8699
8700     if (DelayReached(&sound_delay, sound_delay_value))
8701       sound_delay_value = 30;
8702   }
8703
8704   if (MovDelay[x][y])           /* wait some time before shrinking */
8705   {
8706     MovDelay[x][y]--;
8707     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8708     {
8709       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8710                                            6 - MovDelay[x][y]);
8711
8712       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8713     }
8714
8715     if (!MovDelay[x][y])
8716     {
8717       Feld[x][y] = EL_EMPTY;
8718       TEST_DrawLevelField(x, y);
8719
8720       /* don't let mole enter this field in this cycle;
8721          (give priority to objects falling to this field from above) */
8722       Stop[x][y] = TRUE;
8723     }
8724   }
8725 }
8726
8727 void AmoebeAbleger(int ax, int ay)
8728 {
8729   int i;
8730   int element = Feld[ax][ay];
8731   int graphic = el2img(element);
8732   int newax = ax, neway = ay;
8733   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8734   static int xy[4][2] =
8735   {
8736     { 0, -1 },
8737     { -1, 0 },
8738     { +1, 0 },
8739     { 0, +1 }
8740   };
8741
8742   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8743   {
8744     Feld[ax][ay] = EL_AMOEBA_DEAD;
8745     TEST_DrawLevelField(ax, ay);
8746     return;
8747   }
8748
8749   if (IS_ANIMATED(graphic))
8750     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8751
8752   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8753     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8754
8755   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8756   {
8757     MovDelay[ax][ay]--;
8758     if (MovDelay[ax][ay])
8759       return;
8760   }
8761
8762   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8763   {
8764     int start = RND(4);
8765     int x = ax + xy[start][0];
8766     int y = ay + xy[start][1];
8767
8768     if (!IN_LEV_FIELD(x, y))
8769       return;
8770
8771     if (IS_FREE(x, y) ||
8772         CAN_GROW_INTO(Feld[x][y]) ||
8773         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8774         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8775     {
8776       newax = x;
8777       neway = y;
8778     }
8779
8780     if (newax == ax && neway == ay)
8781       return;
8782   }
8783   else                          /* normal or "filled" (BD style) amoeba */
8784   {
8785     int start = RND(4);
8786     boolean waiting_for_player = FALSE;
8787
8788     for (i = 0; i < NUM_DIRECTIONS; i++)
8789     {
8790       int j = (start + i) % 4;
8791       int x = ax + xy[j][0];
8792       int y = ay + xy[j][1];
8793
8794       if (!IN_LEV_FIELD(x, y))
8795         continue;
8796
8797       if (IS_FREE(x, y) ||
8798           CAN_GROW_INTO(Feld[x][y]) ||
8799           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8800           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8801       {
8802         newax = x;
8803         neway = y;
8804         break;
8805       }
8806       else if (IS_PLAYER(x, y))
8807         waiting_for_player = TRUE;
8808     }
8809
8810     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8811     {
8812       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8813       {
8814         Feld[ax][ay] = EL_AMOEBA_DEAD;
8815         TEST_DrawLevelField(ax, ay);
8816         AmoebaCnt[AmoebaNr[ax][ay]]--;
8817
8818         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8819         {
8820           if (element == EL_AMOEBA_FULL)
8821             AmoebeUmwandeln(ax, ay);
8822           else if (element == EL_BD_AMOEBA)
8823             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8824         }
8825       }
8826       return;
8827     }
8828     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8829     {
8830       /* amoeba gets larger by growing in some direction */
8831
8832       int new_group_nr = AmoebaNr[ax][ay];
8833
8834 #ifdef DEBUG
8835   if (new_group_nr == 0)
8836   {
8837     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8838     printf("AmoebeAbleger(): This should never happen!\n");
8839     return;
8840   }
8841 #endif
8842
8843       AmoebaNr[newax][neway] = new_group_nr;
8844       AmoebaCnt[new_group_nr]++;
8845       AmoebaCnt2[new_group_nr]++;
8846
8847       /* if amoeba touches other amoeba(s) after growing, unify them */
8848       AmoebenVereinigen(newax, neway);
8849
8850       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8851       {
8852         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8853         return;
8854       }
8855     }
8856   }
8857
8858   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8859       (neway == lev_fieldy - 1 && newax != ax))
8860   {
8861     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8862     Store[newax][neway] = element;
8863   }
8864   else if (neway == ay || element == EL_EMC_DRIPPER)
8865   {
8866     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8867
8868     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8869   }
8870   else
8871   {
8872     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8873     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8874     Store[ax][ay] = EL_AMOEBA_DROP;
8875     ContinueMoving(ax, ay);
8876     return;
8877   }
8878
8879   TEST_DrawLevelField(newax, neway);
8880 }
8881
8882 void Life(int ax, int ay)
8883 {
8884   int x1, y1, x2, y2;
8885   int life_time = 40;
8886   int element = Feld[ax][ay];
8887   int graphic = el2img(element);
8888   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8889                          level.biomaze);
8890   boolean changed = FALSE;
8891
8892   if (IS_ANIMATED(graphic))
8893     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8894
8895   if (Stop[ax][ay])
8896     return;
8897
8898   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8899     MovDelay[ax][ay] = life_time;
8900
8901   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8902   {
8903     MovDelay[ax][ay]--;
8904     if (MovDelay[ax][ay])
8905       return;
8906   }
8907
8908   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8909   {
8910     int xx = ax+x1, yy = ay+y1;
8911     int nachbarn = 0;
8912
8913     if (!IN_LEV_FIELD(xx, yy))
8914       continue;
8915
8916     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8917     {
8918       int x = xx+x2, y = yy+y2;
8919
8920       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8921         continue;
8922
8923       if (((Feld[x][y] == element ||
8924             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8925            !Stop[x][y]) ||
8926           (IS_FREE(x, y) && Stop[x][y]))
8927         nachbarn++;
8928     }
8929
8930     if (xx == ax && yy == ay)           /* field in the middle */
8931     {
8932       if (nachbarn < life_parameter[0] ||
8933           nachbarn > life_parameter[1])
8934       {
8935         Feld[xx][yy] = EL_EMPTY;
8936         if (!Stop[xx][yy])
8937           TEST_DrawLevelField(xx, yy);
8938         Stop[xx][yy] = TRUE;
8939         changed = TRUE;
8940       }
8941     }
8942     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8943     {                                   /* free border field */
8944       if (nachbarn >= life_parameter[2] &&
8945           nachbarn <= life_parameter[3])
8946       {
8947         Feld[xx][yy] = element;
8948         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8949         if (!Stop[xx][yy])
8950           TEST_DrawLevelField(xx, yy);
8951         Stop[xx][yy] = TRUE;
8952         changed = TRUE;
8953       }
8954     }
8955   }
8956
8957   if (changed)
8958     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8959                    SND_GAME_OF_LIFE_GROWING);
8960 }
8961
8962 static void InitRobotWheel(int x, int y)
8963 {
8964   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8965 }
8966
8967 static void RunRobotWheel(int x, int y)
8968 {
8969   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8970 }
8971
8972 static void StopRobotWheel(int x, int y)
8973 {
8974   if (ZX == x && ZY == y)
8975   {
8976     ZX = ZY = -1;
8977
8978     game.robot_wheel_active = FALSE;
8979   }
8980 }
8981
8982 static void InitTimegateWheel(int x, int y)
8983 {
8984   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8985 }
8986
8987 static void RunTimegateWheel(int x, int y)
8988 {
8989   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8990 }
8991
8992 static void InitMagicBallDelay(int x, int y)
8993 {
8994   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8995 }
8996
8997 static void ActivateMagicBall(int bx, int by)
8998 {
8999   int x, y;
9000
9001   if (level.ball_random)
9002   {
9003     int pos_border = RND(8);    /* select one of the eight border elements */
9004     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9005     int xx = pos_content % 3;
9006     int yy = pos_content / 3;
9007
9008     x = bx - 1 + xx;
9009     y = by - 1 + yy;
9010
9011     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9012       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9013   }
9014   else
9015   {
9016     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9017     {
9018       int xx = x - bx + 1;
9019       int yy = y - by + 1;
9020
9021       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9022         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9023     }
9024   }
9025
9026   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9027 }
9028
9029 void CheckExit(int x, int y)
9030 {
9031   if (local_player->gems_still_needed > 0 ||
9032       local_player->sokobanfields_still_needed > 0 ||
9033       local_player->lights_still_needed > 0)
9034   {
9035     int element = Feld[x][y];
9036     int graphic = el2img(element);
9037
9038     if (IS_ANIMATED(graphic))
9039       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9040
9041     return;
9042   }
9043
9044   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9045     return;
9046
9047   Feld[x][y] = EL_EXIT_OPENING;
9048
9049   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9050 }
9051
9052 void CheckExitEM(int x, int y)
9053 {
9054   if (local_player->gems_still_needed > 0 ||
9055       local_player->sokobanfields_still_needed > 0 ||
9056       local_player->lights_still_needed > 0)
9057   {
9058     int element = Feld[x][y];
9059     int graphic = el2img(element);
9060
9061     if (IS_ANIMATED(graphic))
9062       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9063
9064     return;
9065   }
9066
9067   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9068     return;
9069
9070   Feld[x][y] = EL_EM_EXIT_OPENING;
9071
9072   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9073 }
9074
9075 void CheckExitSteel(int x, int y)
9076 {
9077   if (local_player->gems_still_needed > 0 ||
9078       local_player->sokobanfields_still_needed > 0 ||
9079       local_player->lights_still_needed > 0)
9080   {
9081     int element = Feld[x][y];
9082     int graphic = el2img(element);
9083
9084     if (IS_ANIMATED(graphic))
9085       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9086
9087     return;
9088   }
9089
9090   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9091     return;
9092
9093   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9094
9095   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9096 }
9097
9098 void CheckExitSteelEM(int x, int y)
9099 {
9100   if (local_player->gems_still_needed > 0 ||
9101       local_player->sokobanfields_still_needed > 0 ||
9102       local_player->lights_still_needed > 0)
9103   {
9104     int element = Feld[x][y];
9105     int graphic = el2img(element);
9106
9107     if (IS_ANIMATED(graphic))
9108       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9109
9110     return;
9111   }
9112
9113   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9114     return;
9115
9116   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9117
9118   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9119 }
9120
9121 void CheckExitSP(int x, int y)
9122 {
9123   if (local_player->gems_still_needed > 0)
9124   {
9125     int element = Feld[x][y];
9126     int graphic = el2img(element);
9127
9128     if (IS_ANIMATED(graphic))
9129       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9130
9131     return;
9132   }
9133
9134   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9135     return;
9136
9137   Feld[x][y] = EL_SP_EXIT_OPENING;
9138
9139   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9140 }
9141
9142 static void CloseAllOpenTimegates()
9143 {
9144   int x, y;
9145
9146   SCAN_PLAYFIELD(x, y)
9147   {
9148     int element = Feld[x][y];
9149
9150     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9151     {
9152       Feld[x][y] = EL_TIMEGATE_CLOSING;
9153
9154       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9155     }
9156   }
9157 }
9158
9159 void DrawTwinkleOnField(int x, int y)
9160 {
9161   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9162     return;
9163
9164   if (Feld[x][y] == EL_BD_DIAMOND)
9165     return;
9166
9167   if (MovDelay[x][y] == 0)      /* next animation frame */
9168     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9169
9170   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9171   {
9172     MovDelay[x][y]--;
9173
9174     DrawLevelElementAnimation(x, y, Feld[x][y]);
9175
9176     if (MovDelay[x][y] != 0)
9177     {
9178       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9179                                            10 - MovDelay[x][y]);
9180
9181       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9182     }
9183   }
9184 }
9185
9186 void MauerWaechst(int x, int y)
9187 {
9188   int delay = 6;
9189
9190   if (!MovDelay[x][y])          /* next animation frame */
9191     MovDelay[x][y] = 3 * delay;
9192
9193   if (MovDelay[x][y])           /* wait some time before next frame */
9194   {
9195     MovDelay[x][y]--;
9196
9197     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9198     {
9199       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9200       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9201
9202       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9203     }
9204
9205     if (!MovDelay[x][y])
9206     {
9207       if (MovDir[x][y] == MV_LEFT)
9208       {
9209         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9210           TEST_DrawLevelField(x - 1, y);
9211       }
9212       else if (MovDir[x][y] == MV_RIGHT)
9213       {
9214         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9215           TEST_DrawLevelField(x + 1, y);
9216       }
9217       else if (MovDir[x][y] == MV_UP)
9218       {
9219         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9220           TEST_DrawLevelField(x, y - 1);
9221       }
9222       else
9223       {
9224         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9225           TEST_DrawLevelField(x, y + 1);
9226       }
9227
9228       Feld[x][y] = Store[x][y];
9229       Store[x][y] = 0;
9230       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9231       TEST_DrawLevelField(x, y);
9232     }
9233   }
9234 }
9235
9236 void MauerAbleger(int ax, int ay)
9237 {
9238   int element = Feld[ax][ay];
9239   int graphic = el2img(element);
9240   boolean oben_frei = FALSE, unten_frei = FALSE;
9241   boolean links_frei = FALSE, rechts_frei = FALSE;
9242   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9243   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9244   boolean new_wall = FALSE;
9245
9246   if (IS_ANIMATED(graphic))
9247     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9248
9249   if (!MovDelay[ax][ay])        /* start building new wall */
9250     MovDelay[ax][ay] = 6;
9251
9252   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9253   {
9254     MovDelay[ax][ay]--;
9255     if (MovDelay[ax][ay])
9256       return;
9257   }
9258
9259   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9260     oben_frei = TRUE;
9261   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9262     unten_frei = TRUE;
9263   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9264     links_frei = TRUE;
9265   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9266     rechts_frei = TRUE;
9267
9268   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9269       element == EL_EXPANDABLE_WALL_ANY)
9270   {
9271     if (oben_frei)
9272     {
9273       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9274       Store[ax][ay-1] = element;
9275       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9276       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9277         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9278                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9279       new_wall = TRUE;
9280     }
9281     if (unten_frei)
9282     {
9283       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9284       Store[ax][ay+1] = element;
9285       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9286       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9287         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9288                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9289       new_wall = TRUE;
9290     }
9291   }
9292
9293   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9294       element == EL_EXPANDABLE_WALL_ANY ||
9295       element == EL_EXPANDABLE_WALL ||
9296       element == EL_BD_EXPANDABLE_WALL)
9297   {
9298     if (links_frei)
9299     {
9300       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9301       Store[ax-1][ay] = element;
9302       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9303       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9304         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9305                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9306       new_wall = TRUE;
9307     }
9308
9309     if (rechts_frei)
9310     {
9311       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9312       Store[ax+1][ay] = element;
9313       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9314       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9315         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9316                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9317       new_wall = TRUE;
9318     }
9319   }
9320
9321   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9322     TEST_DrawLevelField(ax, ay);
9323
9324   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9325     oben_massiv = TRUE;
9326   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9327     unten_massiv = TRUE;
9328   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9329     links_massiv = TRUE;
9330   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9331     rechts_massiv = TRUE;
9332
9333   if (((oben_massiv && unten_massiv) ||
9334        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9335        element == EL_EXPANDABLE_WALL) &&
9336       ((links_massiv && rechts_massiv) ||
9337        element == EL_EXPANDABLE_WALL_VERTICAL))
9338     Feld[ax][ay] = EL_WALL;
9339
9340   if (new_wall)
9341     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9342 }
9343
9344 void MauerAblegerStahl(int ax, int ay)
9345 {
9346   int element = Feld[ax][ay];
9347   int graphic = el2img(element);
9348   boolean oben_frei = FALSE, unten_frei = FALSE;
9349   boolean links_frei = FALSE, rechts_frei = FALSE;
9350   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9351   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9352   boolean new_wall = FALSE;
9353
9354   if (IS_ANIMATED(graphic))
9355     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9356
9357   if (!MovDelay[ax][ay])        /* start building new wall */
9358     MovDelay[ax][ay] = 6;
9359
9360   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9361   {
9362     MovDelay[ax][ay]--;
9363     if (MovDelay[ax][ay])
9364       return;
9365   }
9366
9367   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9368     oben_frei = TRUE;
9369   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9370     unten_frei = TRUE;
9371   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9372     links_frei = TRUE;
9373   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9374     rechts_frei = TRUE;
9375
9376   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9377       element == EL_EXPANDABLE_STEELWALL_ANY)
9378   {
9379     if (oben_frei)
9380     {
9381       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9382       Store[ax][ay-1] = element;
9383       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9384       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9385         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9386                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9387       new_wall = TRUE;
9388     }
9389     if (unten_frei)
9390     {
9391       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9392       Store[ax][ay+1] = element;
9393       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9394       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9395         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9396                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9397       new_wall = TRUE;
9398     }
9399   }
9400
9401   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9402       element == EL_EXPANDABLE_STEELWALL_ANY)
9403   {
9404     if (links_frei)
9405     {
9406       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9407       Store[ax-1][ay] = element;
9408       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9409       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9410         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9411                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9412       new_wall = TRUE;
9413     }
9414
9415     if (rechts_frei)
9416     {
9417       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9418       Store[ax+1][ay] = element;
9419       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9420       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9421         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9422                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9423       new_wall = TRUE;
9424     }
9425   }
9426
9427   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9428     oben_massiv = TRUE;
9429   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9430     unten_massiv = TRUE;
9431   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9432     links_massiv = TRUE;
9433   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9434     rechts_massiv = TRUE;
9435
9436   if (((oben_massiv && unten_massiv) ||
9437        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9438       ((links_massiv && rechts_massiv) ||
9439        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9440     Feld[ax][ay] = EL_STEELWALL;
9441
9442   if (new_wall)
9443     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9444 }
9445
9446 void CheckForDragon(int x, int y)
9447 {
9448   int i, j;
9449   boolean dragon_found = FALSE;
9450   static int xy[4][2] =
9451   {
9452     { 0, -1 },
9453     { -1, 0 },
9454     { +1, 0 },
9455     { 0, +1 }
9456   };
9457
9458   for (i = 0; i < NUM_DIRECTIONS; i++)
9459   {
9460     for (j = 0; j < 4; j++)
9461     {
9462       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9463
9464       if (IN_LEV_FIELD(xx, yy) &&
9465           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9466       {
9467         if (Feld[xx][yy] == EL_DRAGON)
9468           dragon_found = TRUE;
9469       }
9470       else
9471         break;
9472     }
9473   }
9474
9475   if (!dragon_found)
9476   {
9477     for (i = 0; i < NUM_DIRECTIONS; i++)
9478     {
9479       for (j = 0; j < 3; j++)
9480       {
9481         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9482   
9483         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9484         {
9485           Feld[xx][yy] = EL_EMPTY;
9486           TEST_DrawLevelField(xx, yy);
9487         }
9488         else
9489           break;
9490       }
9491     }
9492   }
9493 }
9494
9495 static void InitBuggyBase(int x, int y)
9496 {
9497   int element = Feld[x][y];
9498   int activating_delay = FRAMES_PER_SECOND / 4;
9499
9500   ChangeDelay[x][y] =
9501     (element == EL_SP_BUGGY_BASE ?
9502      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9503      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9504      activating_delay :
9505      element == EL_SP_BUGGY_BASE_ACTIVE ?
9506      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9507 }
9508
9509 static void WarnBuggyBase(int x, int y)
9510 {
9511   int i;
9512   static int xy[4][2] =
9513   {
9514     { 0, -1 },
9515     { -1, 0 },
9516     { +1, 0 },
9517     { 0, +1 }
9518   };
9519
9520   for (i = 0; i < NUM_DIRECTIONS; i++)
9521   {
9522     int xx = x + xy[i][0];
9523     int yy = y + xy[i][1];
9524
9525     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9526     {
9527       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9528
9529       break;
9530     }
9531   }
9532 }
9533
9534 static void InitTrap(int x, int y)
9535 {
9536   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9537 }
9538
9539 static void ActivateTrap(int x, int y)
9540 {
9541   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9542 }
9543
9544 static void ChangeActiveTrap(int x, int y)
9545 {
9546   int graphic = IMG_TRAP_ACTIVE;
9547
9548   /* if new animation frame was drawn, correct crumbled sand border */
9549   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9550     TEST_DrawLevelFieldCrumbled(x, y);
9551 }
9552
9553 static int getSpecialActionElement(int element, int number, int base_element)
9554 {
9555   return (element != EL_EMPTY ? element :
9556           number != -1 ? base_element + number - 1 :
9557           EL_EMPTY);
9558 }
9559
9560 static int getModifiedActionNumber(int value_old, int operator, int operand,
9561                                    int value_min, int value_max)
9562 {
9563   int value_new = (operator == CA_MODE_SET      ? operand :
9564                    operator == CA_MODE_ADD      ? value_old + operand :
9565                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9566                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9567                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9568                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9569                    value_old);
9570
9571   return (value_new < value_min ? value_min :
9572           value_new > value_max ? value_max :
9573           value_new);
9574 }
9575
9576 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9577 {
9578   struct ElementInfo *ei = &element_info[element];
9579   struct ElementChangeInfo *change = &ei->change_page[page];
9580   int target_element = change->target_element;
9581   int action_type = change->action_type;
9582   int action_mode = change->action_mode;
9583   int action_arg = change->action_arg;
9584   int action_element = change->action_element;
9585   int i;
9586
9587   if (!change->has_action)
9588     return;
9589
9590   /* ---------- determine action paramater values -------------------------- */
9591
9592   int level_time_value =
9593     (level.time > 0 ? TimeLeft :
9594      TimePlayed);
9595
9596   int action_arg_element_raw =
9597     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9598      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9599      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9600      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9601      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9602      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9603      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9604      EL_EMPTY);
9605   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9606
9607   int action_arg_direction =
9608     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9609      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9610      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9611      change->actual_trigger_side :
9612      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9613      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9614      MV_NONE);
9615
9616   int action_arg_number_min =
9617     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9618      CA_ARG_MIN);
9619
9620   int action_arg_number_max =
9621     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9622      action_type == CA_SET_LEVEL_GEMS ? 999 :
9623      action_type == CA_SET_LEVEL_TIME ? 9999 :
9624      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9625      action_type == CA_SET_CE_VALUE ? 9999 :
9626      action_type == CA_SET_CE_SCORE ? 9999 :
9627      CA_ARG_MAX);
9628
9629   int action_arg_number_reset =
9630     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9631      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9632      action_type == CA_SET_LEVEL_TIME ? level.time :
9633      action_type == CA_SET_LEVEL_SCORE ? 0 :
9634      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9635      action_type == CA_SET_CE_SCORE ? 0 :
9636      0);
9637
9638   int action_arg_number =
9639     (action_arg <= CA_ARG_MAX ? action_arg :
9640      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9641      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9642      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9643      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9644      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9645      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9646      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9647      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9648      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9649      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9650      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9651      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9652      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9653      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9654      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9655      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9656      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9657      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9658      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9659      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9660      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9661      -1);
9662
9663   int action_arg_number_old =
9664     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9665      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9666      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9667      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9668      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9669      0);
9670
9671   int action_arg_number_new =
9672     getModifiedActionNumber(action_arg_number_old,
9673                             action_mode, action_arg_number,
9674                             action_arg_number_min, action_arg_number_max);
9675
9676   int trigger_player_bits =
9677     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9678      change->actual_trigger_player_bits : change->trigger_player);
9679
9680   int action_arg_player_bits =
9681     (action_arg >= CA_ARG_PLAYER_1 &&
9682      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9683      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9684      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9685      PLAYER_BITS_ANY);
9686
9687   /* ---------- execute action  -------------------------------------------- */
9688
9689   switch (action_type)
9690   {
9691     case CA_NO_ACTION:
9692     {
9693       return;
9694     }
9695
9696     /* ---------- level actions  ------------------------------------------- */
9697
9698     case CA_RESTART_LEVEL:
9699     {
9700       game.restart_level = TRUE;
9701
9702       break;
9703     }
9704
9705     case CA_SHOW_ENVELOPE:
9706     {
9707       int element = getSpecialActionElement(action_arg_element,
9708                                             action_arg_number, EL_ENVELOPE_1);
9709
9710       if (IS_ENVELOPE(element))
9711         local_player->show_envelope = element;
9712
9713       break;
9714     }
9715
9716     case CA_SET_LEVEL_TIME:
9717     {
9718       if (level.time > 0)       /* only modify limited time value */
9719       {
9720         TimeLeft = action_arg_number_new;
9721
9722         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9723
9724         DisplayGameControlValues();
9725
9726         if (!TimeLeft && setup.time_limit)
9727           for (i = 0; i < MAX_PLAYERS; i++)
9728             KillPlayer(&stored_player[i]);
9729       }
9730
9731       break;
9732     }
9733
9734     case CA_SET_LEVEL_SCORE:
9735     {
9736       local_player->score = action_arg_number_new;
9737
9738       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9739
9740       DisplayGameControlValues();
9741
9742       break;
9743     }
9744
9745     case CA_SET_LEVEL_GEMS:
9746     {
9747       local_player->gems_still_needed = action_arg_number_new;
9748
9749       game.snapshot.collected_item = TRUE;
9750
9751       game_panel_controls[GAME_PANEL_GEMS].value =
9752         local_player->gems_still_needed;
9753
9754       DisplayGameControlValues();
9755
9756       break;
9757     }
9758
9759     case CA_SET_LEVEL_WIND:
9760     {
9761       game.wind_direction = action_arg_direction;
9762
9763       break;
9764     }
9765
9766     case CA_SET_LEVEL_RANDOM_SEED:
9767     {
9768       /* ensure that setting a new random seed while playing is predictable */
9769       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9770
9771       break;
9772     }
9773
9774     /* ---------- player actions  ------------------------------------------ */
9775
9776     case CA_MOVE_PLAYER:
9777     {
9778       /* automatically move to the next field in specified direction */
9779       for (i = 0; i < MAX_PLAYERS; i++)
9780         if (trigger_player_bits & (1 << i))
9781           stored_player[i].programmed_action = action_arg_direction;
9782
9783       break;
9784     }
9785
9786     case CA_EXIT_PLAYER:
9787     {
9788       for (i = 0; i < MAX_PLAYERS; i++)
9789         if (action_arg_player_bits & (1 << i))
9790           ExitPlayer(&stored_player[i]);
9791
9792       if (AllPlayersGone)
9793         PlayerWins(local_player);
9794
9795       break;
9796     }
9797
9798     case CA_KILL_PLAYER:
9799     {
9800       for (i = 0; i < MAX_PLAYERS; i++)
9801         if (action_arg_player_bits & (1 << i))
9802           KillPlayer(&stored_player[i]);
9803
9804       break;
9805     }
9806
9807     case CA_SET_PLAYER_KEYS:
9808     {
9809       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9810       int element = getSpecialActionElement(action_arg_element,
9811                                             action_arg_number, EL_KEY_1);
9812
9813       if (IS_KEY(element))
9814       {
9815         for (i = 0; i < MAX_PLAYERS; i++)
9816         {
9817           if (trigger_player_bits & (1 << i))
9818           {
9819             stored_player[i].key[KEY_NR(element)] = key_state;
9820
9821             DrawGameDoorValues();
9822           }
9823         }
9824       }
9825
9826       break;
9827     }
9828
9829     case CA_SET_PLAYER_SPEED:
9830     {
9831       for (i = 0; i < MAX_PLAYERS; i++)
9832       {
9833         if (trigger_player_bits & (1 << i))
9834         {
9835           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9836
9837           if (action_arg == CA_ARG_SPEED_FASTER &&
9838               stored_player[i].cannot_move)
9839           {
9840             action_arg_number = STEPSIZE_VERY_SLOW;
9841           }
9842           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9843                    action_arg == CA_ARG_SPEED_FASTER)
9844           {
9845             action_arg_number = 2;
9846             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9847                            CA_MODE_MULTIPLY);
9848           }
9849           else if (action_arg == CA_ARG_NUMBER_RESET)
9850           {
9851             action_arg_number = level.initial_player_stepsize[i];
9852           }
9853
9854           move_stepsize =
9855             getModifiedActionNumber(move_stepsize,
9856                                     action_mode,
9857                                     action_arg_number,
9858                                     action_arg_number_min,
9859                                     action_arg_number_max);
9860
9861           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9862         }
9863       }
9864
9865       break;
9866     }
9867
9868     case CA_SET_PLAYER_SHIELD:
9869     {
9870       for (i = 0; i < MAX_PLAYERS; i++)
9871       {
9872         if (trigger_player_bits & (1 << i))
9873         {
9874           if (action_arg == CA_ARG_SHIELD_OFF)
9875           {
9876             stored_player[i].shield_normal_time_left = 0;
9877             stored_player[i].shield_deadly_time_left = 0;
9878           }
9879           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9880           {
9881             stored_player[i].shield_normal_time_left = 999999;
9882           }
9883           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9884           {
9885             stored_player[i].shield_normal_time_left = 999999;
9886             stored_player[i].shield_deadly_time_left = 999999;
9887           }
9888         }
9889       }
9890
9891       break;
9892     }
9893
9894     case CA_SET_PLAYER_GRAVITY:
9895     {
9896       for (i = 0; i < MAX_PLAYERS; i++)
9897       {
9898         if (trigger_player_bits & (1 << i))
9899         {
9900           stored_player[i].gravity =
9901             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9902              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9903              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9904              stored_player[i].gravity);
9905         }
9906       }
9907
9908       break;
9909     }
9910
9911     case CA_SET_PLAYER_ARTWORK:
9912     {
9913       for (i = 0; i < MAX_PLAYERS; i++)
9914       {
9915         if (trigger_player_bits & (1 << i))
9916         {
9917           int artwork_element = action_arg_element;
9918
9919           if (action_arg == CA_ARG_ELEMENT_RESET)
9920             artwork_element =
9921               (level.use_artwork_element[i] ? level.artwork_element[i] :
9922                stored_player[i].element_nr);
9923
9924           if (stored_player[i].artwork_element != artwork_element)
9925             stored_player[i].Frame = 0;
9926
9927           stored_player[i].artwork_element = artwork_element;
9928
9929           SetPlayerWaiting(&stored_player[i], FALSE);
9930
9931           /* set number of special actions for bored and sleeping animation */
9932           stored_player[i].num_special_action_bored =
9933             get_num_special_action(artwork_element,
9934                                    ACTION_BORING_1, ACTION_BORING_LAST);
9935           stored_player[i].num_special_action_sleeping =
9936             get_num_special_action(artwork_element,
9937                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9938         }
9939       }
9940
9941       break;
9942     }
9943
9944     case CA_SET_PLAYER_INVENTORY:
9945     {
9946       for (i = 0; i < MAX_PLAYERS; i++)
9947       {
9948         struct PlayerInfo *player = &stored_player[i];
9949         int j, k;
9950
9951         if (trigger_player_bits & (1 << i))
9952         {
9953           int inventory_element = action_arg_element;
9954
9955           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9956               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9957               action_arg == CA_ARG_ELEMENT_ACTION)
9958           {
9959             int element = inventory_element;
9960             int collect_count = element_info[element].collect_count_initial;
9961
9962             if (!IS_CUSTOM_ELEMENT(element))
9963               collect_count = 1;
9964
9965             if (collect_count == 0)
9966               player->inventory_infinite_element = element;
9967             else
9968               for (k = 0; k < collect_count; k++)
9969                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9970                   player->inventory_element[player->inventory_size++] =
9971                     element;
9972           }
9973           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9974                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9975                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9976           {
9977             if (player->inventory_infinite_element != EL_UNDEFINED &&
9978                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9979                                      action_arg_element_raw))
9980               player->inventory_infinite_element = EL_UNDEFINED;
9981
9982             for (k = 0, j = 0; j < player->inventory_size; j++)
9983             {
9984               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9985                                         action_arg_element_raw))
9986                 player->inventory_element[k++] = player->inventory_element[j];
9987             }
9988
9989             player->inventory_size = k;
9990           }
9991           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9992           {
9993             if (player->inventory_size > 0)
9994             {
9995               for (j = 0; j < player->inventory_size - 1; j++)
9996                 player->inventory_element[j] = player->inventory_element[j + 1];
9997
9998               player->inventory_size--;
9999             }
10000           }
10001           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10002           {
10003             if (player->inventory_size > 0)
10004               player->inventory_size--;
10005           }
10006           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10007           {
10008             player->inventory_infinite_element = EL_UNDEFINED;
10009             player->inventory_size = 0;
10010           }
10011           else if (action_arg == CA_ARG_INVENTORY_RESET)
10012           {
10013             player->inventory_infinite_element = EL_UNDEFINED;
10014             player->inventory_size = 0;
10015
10016             if (level.use_initial_inventory[i])
10017             {
10018               for (j = 0; j < level.initial_inventory_size[i]; j++)
10019               {
10020                 int element = level.initial_inventory_content[i][j];
10021                 int collect_count = element_info[element].collect_count_initial;
10022
10023                 if (!IS_CUSTOM_ELEMENT(element))
10024                   collect_count = 1;
10025
10026                 if (collect_count == 0)
10027                   player->inventory_infinite_element = element;
10028                 else
10029                   for (k = 0; k < collect_count; k++)
10030                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10031                       player->inventory_element[player->inventory_size++] =
10032                         element;
10033               }
10034             }
10035           }
10036         }
10037       }
10038
10039       break;
10040     }
10041
10042     /* ---------- CE actions  ---------------------------------------------- */
10043
10044     case CA_SET_CE_VALUE:
10045     {
10046       int last_ce_value = CustomValue[x][y];
10047
10048       CustomValue[x][y] = action_arg_number_new;
10049
10050       if (CustomValue[x][y] != last_ce_value)
10051       {
10052         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10053         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10054
10055         if (CustomValue[x][y] == 0)
10056         {
10057           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10058           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10059         }
10060       }
10061
10062       break;
10063     }
10064
10065     case CA_SET_CE_SCORE:
10066     {
10067       int last_ce_score = ei->collect_score;
10068
10069       ei->collect_score = action_arg_number_new;
10070
10071       if (ei->collect_score != last_ce_score)
10072       {
10073         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10074         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10075
10076         if (ei->collect_score == 0)
10077         {
10078           int xx, yy;
10079
10080           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10081           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10082
10083           /*
10084             This is a very special case that seems to be a mixture between
10085             CheckElementChange() and CheckTriggeredElementChange(): while
10086             the first one only affects single elements that are triggered
10087             directly, the second one affects multiple elements in the playfield
10088             that are triggered indirectly by another element. This is a third
10089             case: Changing the CE score always affects multiple identical CEs,
10090             so every affected CE must be checked, not only the single CE for
10091             which the CE score was changed in the first place (as every instance
10092             of that CE shares the same CE score, and therefore also can change)!
10093           */
10094           SCAN_PLAYFIELD(xx, yy)
10095           {
10096             if (Feld[xx][yy] == element)
10097               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10098                                  CE_SCORE_GETS_ZERO);
10099           }
10100         }
10101       }
10102
10103       break;
10104     }
10105
10106     case CA_SET_CE_ARTWORK:
10107     {
10108       int artwork_element = action_arg_element;
10109       boolean reset_frame = FALSE;
10110       int xx, yy;
10111
10112       if (action_arg == CA_ARG_ELEMENT_RESET)
10113         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10114                            element);
10115
10116       if (ei->gfx_element != artwork_element)
10117         reset_frame = TRUE;
10118
10119       ei->gfx_element = artwork_element;
10120
10121       SCAN_PLAYFIELD(xx, yy)
10122       {
10123         if (Feld[xx][yy] == element)
10124         {
10125           if (reset_frame)
10126           {
10127             ResetGfxAnimation(xx, yy);
10128             ResetRandomAnimationValue(xx, yy);
10129           }
10130
10131           TEST_DrawLevelField(xx, yy);
10132         }
10133       }
10134
10135       break;
10136     }
10137
10138     /* ---------- engine actions  ------------------------------------------ */
10139
10140     case CA_SET_ENGINE_SCAN_MODE:
10141     {
10142       InitPlayfieldScanMode(action_arg);
10143
10144       break;
10145     }
10146
10147     default:
10148       break;
10149   }
10150 }
10151
10152 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10153 {
10154   int old_element = Feld[x][y];
10155   int new_element = GetElementFromGroupElement(element);
10156   int previous_move_direction = MovDir[x][y];
10157   int last_ce_value = CustomValue[x][y];
10158   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10159   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10160   boolean add_player_onto_element = (new_element_is_player &&
10161                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10162                                      IS_WALKABLE(old_element));
10163
10164   if (!add_player_onto_element)
10165   {
10166     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10167       RemoveMovingField(x, y);
10168     else
10169       RemoveField(x, y);
10170
10171     Feld[x][y] = new_element;
10172
10173     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10174       MovDir[x][y] = previous_move_direction;
10175
10176     if (element_info[new_element].use_last_ce_value)
10177       CustomValue[x][y] = last_ce_value;
10178
10179     InitField_WithBug1(x, y, FALSE);
10180
10181     new_element = Feld[x][y];   /* element may have changed */
10182
10183     ResetGfxAnimation(x, y);
10184     ResetRandomAnimationValue(x, y);
10185
10186     TEST_DrawLevelField(x, y);
10187
10188     if (GFX_CRUMBLED(new_element))
10189       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10190   }
10191
10192   /* check if element under the player changes from accessible to unaccessible
10193      (needed for special case of dropping element which then changes) */
10194   /* (must be checked after creating new element for walkable group elements) */
10195   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10196       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10197   {
10198     Bang(x, y);
10199
10200     return;
10201   }
10202
10203   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10204   if (new_element_is_player)
10205     RelocatePlayer(x, y, new_element);
10206
10207   if (is_change)
10208     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10209
10210   TestIfBadThingTouchesPlayer(x, y);
10211   TestIfPlayerTouchesCustomElement(x, y);
10212   TestIfElementTouchesCustomElement(x, y);
10213 }
10214
10215 static void CreateField(int x, int y, int element)
10216 {
10217   CreateFieldExt(x, y, element, FALSE);
10218 }
10219
10220 static void CreateElementFromChange(int x, int y, int element)
10221 {
10222   element = GET_VALID_RUNTIME_ELEMENT(element);
10223
10224   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10225   {
10226     int old_element = Feld[x][y];
10227
10228     /* prevent changed element from moving in same engine frame
10229        unless both old and new element can either fall or move */
10230     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10231         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10232       Stop[x][y] = TRUE;
10233   }
10234
10235   CreateFieldExt(x, y, element, TRUE);
10236 }
10237
10238 static boolean ChangeElement(int x, int y, int element, int page)
10239 {
10240   struct ElementInfo *ei = &element_info[element];
10241   struct ElementChangeInfo *change = &ei->change_page[page];
10242   int ce_value = CustomValue[x][y];
10243   int ce_score = ei->collect_score;
10244   int target_element;
10245   int old_element = Feld[x][y];
10246
10247   /* always use default change event to prevent running into a loop */
10248   if (ChangeEvent[x][y] == -1)
10249     ChangeEvent[x][y] = CE_DELAY;
10250
10251   if (ChangeEvent[x][y] == CE_DELAY)
10252   {
10253     /* reset actual trigger element, trigger player and action element */
10254     change->actual_trigger_element = EL_EMPTY;
10255     change->actual_trigger_player = EL_EMPTY;
10256     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10257     change->actual_trigger_side = CH_SIDE_NONE;
10258     change->actual_trigger_ce_value = 0;
10259     change->actual_trigger_ce_score = 0;
10260   }
10261
10262   /* do not change elements more than a specified maximum number of changes */
10263   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10264     return FALSE;
10265
10266   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10267
10268   if (change->explode)
10269   {
10270     Bang(x, y);
10271
10272     return TRUE;
10273   }
10274
10275   if (change->use_target_content)
10276   {
10277     boolean complete_replace = TRUE;
10278     boolean can_replace[3][3];
10279     int xx, yy;
10280
10281     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10282     {
10283       boolean is_empty;
10284       boolean is_walkable;
10285       boolean is_diggable;
10286       boolean is_collectible;
10287       boolean is_removable;
10288       boolean is_destructible;
10289       int ex = x + xx - 1;
10290       int ey = y + yy - 1;
10291       int content_element = change->target_content.e[xx][yy];
10292       int e;
10293
10294       can_replace[xx][yy] = TRUE;
10295
10296       if (ex == x && ey == y)   /* do not check changing element itself */
10297         continue;
10298
10299       if (content_element == EL_EMPTY_SPACE)
10300       {
10301         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10302
10303         continue;
10304       }
10305
10306       if (!IN_LEV_FIELD(ex, ey))
10307       {
10308         can_replace[xx][yy] = FALSE;
10309         complete_replace = FALSE;
10310
10311         continue;
10312       }
10313
10314       e = Feld[ex][ey];
10315
10316       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10317         e = MovingOrBlocked2Element(ex, ey);
10318
10319       is_empty = (IS_FREE(ex, ey) ||
10320                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10321
10322       is_walkable     = (is_empty || IS_WALKABLE(e));
10323       is_diggable     = (is_empty || IS_DIGGABLE(e));
10324       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10325       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10326       is_removable    = (is_diggable || is_collectible);
10327
10328       can_replace[xx][yy] =
10329         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10330           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10331           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10332           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10333           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10334           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10335          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10336
10337       if (!can_replace[xx][yy])
10338         complete_replace = FALSE;
10339     }
10340
10341     if (!change->only_if_complete || complete_replace)
10342     {
10343       boolean something_has_changed = FALSE;
10344
10345       if (change->only_if_complete && change->use_random_replace &&
10346           RND(100) < change->random_percentage)
10347         return FALSE;
10348
10349       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10350       {
10351         int ex = x + xx - 1;
10352         int ey = y + yy - 1;
10353         int content_element;
10354
10355         if (can_replace[xx][yy] && (!change->use_random_replace ||
10356                                     RND(100) < change->random_percentage))
10357         {
10358           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10359             RemoveMovingField(ex, ey);
10360
10361           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10362
10363           content_element = change->target_content.e[xx][yy];
10364           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10365                                               ce_value, ce_score);
10366
10367           CreateElementFromChange(ex, ey, target_element);
10368
10369           something_has_changed = TRUE;
10370
10371           /* for symmetry reasons, freeze newly created border elements */
10372           if (ex != x || ey != y)
10373             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10374         }
10375       }
10376
10377       if (something_has_changed)
10378       {
10379         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10380         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10381       }
10382     }
10383   }
10384   else
10385   {
10386     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10387                                         ce_value, ce_score);
10388
10389     if (element == EL_DIAGONAL_GROWING ||
10390         element == EL_DIAGONAL_SHRINKING)
10391     {
10392       target_element = Store[x][y];
10393
10394       Store[x][y] = EL_EMPTY;
10395     }
10396
10397     CreateElementFromChange(x, y, target_element);
10398
10399     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10400     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10401   }
10402
10403   /* this uses direct change before indirect change */
10404   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10405
10406   return TRUE;
10407 }
10408
10409 static void HandleElementChange(int x, int y, int page)
10410 {
10411   int element = MovingOrBlocked2Element(x, y);
10412   struct ElementInfo *ei = &element_info[element];
10413   struct ElementChangeInfo *change = &ei->change_page[page];
10414   boolean handle_action_before_change = FALSE;
10415
10416 #ifdef DEBUG
10417   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10418       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10419   {
10420     printf("\n\n");
10421     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10422            x, y, element, element_info[element].token_name);
10423     printf("HandleElementChange(): This should never happen!\n");
10424     printf("\n\n");
10425   }
10426 #endif
10427
10428   /* this can happen with classic bombs on walkable, changing elements */
10429   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10430   {
10431     return;
10432   }
10433
10434   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10435   {
10436     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10437
10438     if (change->can_change)
10439     {
10440       /* !!! not clear why graphic animation should be reset at all here !!! */
10441       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10442       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10443
10444       /*
10445         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10446
10447         When using an animation frame delay of 1 (this only happens with
10448         "sp_zonk.moving.left/right" in the classic graphics), the default
10449         (non-moving) animation shows wrong animation frames (while the
10450         moving animation, like "sp_zonk.moving.left/right", is correct,
10451         so this graphical bug never shows up with the classic graphics).
10452         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10453         be drawn instead of the correct frames 0,1,2,3. This is caused by
10454         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10455         an element change: First when the change delay ("ChangeDelay[][]")
10456         counter has reached zero after decrementing, then a second time in
10457         the next frame (after "GfxFrame[][]" was already incremented) when
10458         "ChangeDelay[][]" is reset to the initial delay value again.
10459
10460         This causes frame 0 to be drawn twice, while the last frame won't
10461         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10462
10463         As some animations may already be cleverly designed around this bug
10464         (at least the "Snake Bite" snake tail animation does this), it cannot
10465         simply be fixed here without breaking such existing animations.
10466         Unfortunately, it cannot easily be detected if a graphics set was
10467         designed "before" or "after" the bug was fixed. As a workaround,
10468         a new graphics set option "game.graphics_engine_version" was added
10469         to be able to specify the game's major release version for which the
10470         graphics set was designed, which can then be used to decide if the
10471         bugfix should be used (version 4 and above) or not (version 3 or
10472         below, or if no version was specified at all, as with old sets).
10473
10474         (The wrong/fixed animation frames can be tested with the test level set
10475         "test_gfxframe" and level "000", which contains a specially prepared
10476         custom element at level position (x/y) == (11/9) which uses the zonk
10477         animation mentioned above. Using "game.graphics_engine_version: 4"
10478         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10479         This can also be seen from the debug output for this test element.)
10480       */
10481
10482       /* when a custom element is about to change (for example by change delay),
10483          do not reset graphic animation when the custom element is moving */
10484       if (game.graphics_engine_version < 4 &&
10485           !IS_MOVING(x, y))
10486       {
10487         ResetGfxAnimation(x, y);
10488         ResetRandomAnimationValue(x, y);
10489       }
10490
10491       if (change->pre_change_function)
10492         change->pre_change_function(x, y);
10493     }
10494   }
10495
10496   ChangeDelay[x][y]--;
10497
10498   if (ChangeDelay[x][y] != 0)           /* continue element change */
10499   {
10500     if (change->can_change)
10501     {
10502       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10503
10504       if (IS_ANIMATED(graphic))
10505         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10506
10507       if (change->change_function)
10508         change->change_function(x, y);
10509     }
10510   }
10511   else                                  /* finish element change */
10512   {
10513     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10514     {
10515       page = ChangePage[x][y];
10516       ChangePage[x][y] = -1;
10517
10518       change = &ei->change_page[page];
10519     }
10520
10521     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10522     {
10523       ChangeDelay[x][y] = 1;            /* try change after next move step */
10524       ChangePage[x][y] = page;          /* remember page to use for change */
10525
10526       return;
10527     }
10528
10529     /* special case: set new level random seed before changing element */
10530     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10531       handle_action_before_change = TRUE;
10532
10533     if (change->has_action && handle_action_before_change)
10534       ExecuteCustomElementAction(x, y, element, page);
10535
10536     if (change->can_change)
10537     {
10538       if (ChangeElement(x, y, element, page))
10539       {
10540         if (change->post_change_function)
10541           change->post_change_function(x, y);
10542       }
10543     }
10544
10545     if (change->has_action && !handle_action_before_change)
10546       ExecuteCustomElementAction(x, y, element, page);
10547   }
10548 }
10549
10550 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10551                                               int trigger_element,
10552                                               int trigger_event,
10553                                               int trigger_player,
10554                                               int trigger_side,
10555                                               int trigger_page)
10556 {
10557   boolean change_done_any = FALSE;
10558   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10559   int i;
10560
10561   if (!(trigger_events[trigger_element][trigger_event]))
10562     return FALSE;
10563
10564   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10565
10566   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10567   {
10568     int element = EL_CUSTOM_START + i;
10569     boolean change_done = FALSE;
10570     int p;
10571
10572     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10573         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10574       continue;
10575
10576     for (p = 0; p < element_info[element].num_change_pages; p++)
10577     {
10578       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10579
10580       if (change->can_change_or_has_action &&
10581           change->has_event[trigger_event] &&
10582           change->trigger_side & trigger_side &&
10583           change->trigger_player & trigger_player &&
10584           change->trigger_page & trigger_page_bits &&
10585           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10586       {
10587         change->actual_trigger_element = trigger_element;
10588         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10589         change->actual_trigger_player_bits = trigger_player;
10590         change->actual_trigger_side = trigger_side;
10591         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10592         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10593
10594         if ((change->can_change && !change_done) || change->has_action)
10595         {
10596           int x, y;
10597
10598           SCAN_PLAYFIELD(x, y)
10599           {
10600             if (Feld[x][y] == element)
10601             {
10602               if (change->can_change && !change_done)
10603               {
10604                 /* if element already changed in this frame, not only prevent
10605                    another element change (checked in ChangeElement()), but
10606                    also prevent additional element actions for this element */
10607
10608                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10609                     !level.use_action_after_change_bug)
10610                   continue;
10611
10612                 ChangeDelay[x][y] = 1;
10613                 ChangeEvent[x][y] = trigger_event;
10614
10615                 HandleElementChange(x, y, p);
10616               }
10617               else if (change->has_action)
10618               {
10619                 /* if element already changed in this frame, not only prevent
10620                    another element change (checked in ChangeElement()), but
10621                    also prevent additional element actions for this element */
10622
10623                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10624                     !level.use_action_after_change_bug)
10625                   continue;
10626
10627                 ExecuteCustomElementAction(x, y, element, p);
10628                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10629               }
10630             }
10631           }
10632
10633           if (change->can_change)
10634           {
10635             change_done = TRUE;
10636             change_done_any = TRUE;
10637           }
10638         }
10639       }
10640     }
10641   }
10642
10643   RECURSION_LOOP_DETECTION_END();
10644
10645   return change_done_any;
10646 }
10647
10648 static boolean CheckElementChangeExt(int x, int y,
10649                                      int element,
10650                                      int trigger_element,
10651                                      int trigger_event,
10652                                      int trigger_player,
10653                                      int trigger_side)
10654 {
10655   boolean change_done = FALSE;
10656   int p;
10657
10658   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10659       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10660     return FALSE;
10661
10662   if (Feld[x][y] == EL_BLOCKED)
10663   {
10664     Blocked2Moving(x, y, &x, &y);
10665     element = Feld[x][y];
10666   }
10667
10668   /* check if element has already changed or is about to change after moving */
10669   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10670        Feld[x][y] != element) ||
10671
10672       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10673        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10674         ChangePage[x][y] != -1)))
10675     return FALSE;
10676
10677   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10678
10679   for (p = 0; p < element_info[element].num_change_pages; p++)
10680   {
10681     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10682
10683     /* check trigger element for all events where the element that is checked
10684        for changing interacts with a directly adjacent element -- this is
10685        different to element changes that affect other elements to change on the
10686        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10687     boolean check_trigger_element =
10688       (trigger_event == CE_TOUCHING_X ||
10689        trigger_event == CE_HITTING_X ||
10690        trigger_event == CE_HIT_BY_X ||
10691        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10692
10693     if (change->can_change_or_has_action &&
10694         change->has_event[trigger_event] &&
10695         change->trigger_side & trigger_side &&
10696         change->trigger_player & trigger_player &&
10697         (!check_trigger_element ||
10698          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10699     {
10700       change->actual_trigger_element = trigger_element;
10701       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10702       change->actual_trigger_player_bits = trigger_player;
10703       change->actual_trigger_side = trigger_side;
10704       change->actual_trigger_ce_value = CustomValue[x][y];
10705       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10706
10707       /* special case: trigger element not at (x,y) position for some events */
10708       if (check_trigger_element)
10709       {
10710         static struct
10711         {
10712           int dx, dy;
10713         } move_xy[] =
10714           {
10715             {  0,  0 },
10716             { -1,  0 },
10717             { +1,  0 },
10718             {  0,  0 },
10719             {  0, -1 },
10720             {  0,  0 }, { 0, 0 }, { 0, 0 },
10721             {  0, +1 }
10722           };
10723
10724         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10725         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10726
10727         change->actual_trigger_ce_value = CustomValue[xx][yy];
10728         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10729       }
10730
10731       if (change->can_change && !change_done)
10732       {
10733         ChangeDelay[x][y] = 1;
10734         ChangeEvent[x][y] = trigger_event;
10735
10736         HandleElementChange(x, y, p);
10737
10738         change_done = TRUE;
10739       }
10740       else if (change->has_action)
10741       {
10742         ExecuteCustomElementAction(x, y, element, p);
10743         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10744       }
10745     }
10746   }
10747
10748   RECURSION_LOOP_DETECTION_END();
10749
10750   return change_done;
10751 }
10752
10753 static void PlayPlayerSound(struct PlayerInfo *player)
10754 {
10755   int jx = player->jx, jy = player->jy;
10756   int sound_element = player->artwork_element;
10757   int last_action = player->last_action_waiting;
10758   int action = player->action_waiting;
10759
10760   if (player->is_waiting)
10761   {
10762     if (action != last_action)
10763       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10764     else
10765       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10766   }
10767   else
10768   {
10769     if (action != last_action)
10770       StopSound(element_info[sound_element].sound[last_action]);
10771
10772     if (last_action == ACTION_SLEEPING)
10773       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10774   }
10775 }
10776
10777 static void PlayAllPlayersSound()
10778 {
10779   int i;
10780
10781   for (i = 0; i < MAX_PLAYERS; i++)
10782     if (stored_player[i].active)
10783       PlayPlayerSound(&stored_player[i]);
10784 }
10785
10786 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10787 {
10788   boolean last_waiting = player->is_waiting;
10789   int move_dir = player->MovDir;
10790
10791   player->dir_waiting = move_dir;
10792   player->last_action_waiting = player->action_waiting;
10793
10794   if (is_waiting)
10795   {
10796     if (!last_waiting)          /* not waiting -> waiting */
10797     {
10798       player->is_waiting = TRUE;
10799
10800       player->frame_counter_bored =
10801         FrameCounter +
10802         game.player_boring_delay_fixed +
10803         GetSimpleRandom(game.player_boring_delay_random);
10804       player->frame_counter_sleeping =
10805         FrameCounter +
10806         game.player_sleeping_delay_fixed +
10807         GetSimpleRandom(game.player_sleeping_delay_random);
10808
10809       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10810     }
10811
10812     if (game.player_sleeping_delay_fixed +
10813         game.player_sleeping_delay_random > 0 &&
10814         player->anim_delay_counter == 0 &&
10815         player->post_delay_counter == 0 &&
10816         FrameCounter >= player->frame_counter_sleeping)
10817       player->is_sleeping = TRUE;
10818     else if (game.player_boring_delay_fixed +
10819              game.player_boring_delay_random > 0 &&
10820              FrameCounter >= player->frame_counter_bored)
10821       player->is_bored = TRUE;
10822
10823     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10824                               player->is_bored ? ACTION_BORING :
10825                               ACTION_WAITING);
10826
10827     if (player->is_sleeping && player->use_murphy)
10828     {
10829       /* special case for sleeping Murphy when leaning against non-free tile */
10830
10831       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10832           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10833            !IS_MOVING(player->jx - 1, player->jy)))
10834         move_dir = MV_LEFT;
10835       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10836                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10837                 !IS_MOVING(player->jx + 1, player->jy)))
10838         move_dir = MV_RIGHT;
10839       else
10840         player->is_sleeping = FALSE;
10841
10842       player->dir_waiting = move_dir;
10843     }
10844
10845     if (player->is_sleeping)
10846     {
10847       if (player->num_special_action_sleeping > 0)
10848       {
10849         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10850         {
10851           int last_special_action = player->special_action_sleeping;
10852           int num_special_action = player->num_special_action_sleeping;
10853           int special_action =
10854             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10855              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10856              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10857              last_special_action + 1 : ACTION_SLEEPING);
10858           int special_graphic =
10859             el_act_dir2img(player->artwork_element, special_action, move_dir);
10860
10861           player->anim_delay_counter =
10862             graphic_info[special_graphic].anim_delay_fixed +
10863             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10864           player->post_delay_counter =
10865             graphic_info[special_graphic].post_delay_fixed +
10866             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10867
10868           player->special_action_sleeping = special_action;
10869         }
10870
10871         if (player->anim_delay_counter > 0)
10872         {
10873           player->action_waiting = player->special_action_sleeping;
10874           player->anim_delay_counter--;
10875         }
10876         else if (player->post_delay_counter > 0)
10877         {
10878           player->post_delay_counter--;
10879         }
10880       }
10881     }
10882     else if (player->is_bored)
10883     {
10884       if (player->num_special_action_bored > 0)
10885       {
10886         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10887         {
10888           int special_action =
10889             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10890           int special_graphic =
10891             el_act_dir2img(player->artwork_element, special_action, move_dir);
10892
10893           player->anim_delay_counter =
10894             graphic_info[special_graphic].anim_delay_fixed +
10895             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10896           player->post_delay_counter =
10897             graphic_info[special_graphic].post_delay_fixed +
10898             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10899
10900           player->special_action_bored = special_action;
10901         }
10902
10903         if (player->anim_delay_counter > 0)
10904         {
10905           player->action_waiting = player->special_action_bored;
10906           player->anim_delay_counter--;
10907         }
10908         else if (player->post_delay_counter > 0)
10909         {
10910           player->post_delay_counter--;
10911         }
10912       }
10913     }
10914   }
10915   else if (last_waiting)        /* waiting -> not waiting */
10916   {
10917     player->is_waiting = FALSE;
10918     player->is_bored = FALSE;
10919     player->is_sleeping = FALSE;
10920
10921     player->frame_counter_bored = -1;
10922     player->frame_counter_sleeping = -1;
10923
10924     player->anim_delay_counter = 0;
10925     player->post_delay_counter = 0;
10926
10927     player->dir_waiting = player->MovDir;
10928     player->action_waiting = ACTION_DEFAULT;
10929
10930     player->special_action_bored = ACTION_DEFAULT;
10931     player->special_action_sleeping = ACTION_DEFAULT;
10932   }
10933 }
10934
10935 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10936 {
10937   if ((!player->is_moving  && player->was_moving) ||
10938       (player->MovPos == 0 && player->was_moving) ||
10939       (player->is_snapping && !player->was_snapping) ||
10940       (player->is_dropping && !player->was_dropping))
10941   {
10942     if (!CheckSaveEngineSnapshotToList())
10943       return;
10944
10945     player->was_moving = FALSE;
10946     player->was_snapping = TRUE;
10947     player->was_dropping = TRUE;
10948   }
10949   else
10950   {
10951     if (player->is_moving)
10952       player->was_moving = TRUE;
10953
10954     if (!player->is_snapping)
10955       player->was_snapping = FALSE;
10956
10957     if (!player->is_dropping)
10958       player->was_dropping = FALSE;
10959   }
10960 }
10961
10962 static void CheckSingleStepMode(struct PlayerInfo *player)
10963 {
10964   if (tape.single_step && tape.recording && !tape.pausing)
10965   {
10966     /* as it is called "single step mode", just return to pause mode when the
10967        player stopped moving after one tile (or never starts moving at all) */
10968     if (!player->is_moving &&
10969         !player->is_pushing &&
10970         !player->is_dropping_pressed)
10971     {
10972       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10973       SnapField(player, 0, 0);                  /* stop snapping */
10974     }
10975   }
10976
10977   CheckSaveEngineSnapshot(player);
10978 }
10979
10980 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10981 {
10982   int left      = player_action & JOY_LEFT;
10983   int right     = player_action & JOY_RIGHT;
10984   int up        = player_action & JOY_UP;
10985   int down      = player_action & JOY_DOWN;
10986   int button1   = player_action & JOY_BUTTON_1;
10987   int button2   = player_action & JOY_BUTTON_2;
10988   int dx        = (left ? -1 : right ? 1 : 0);
10989   int dy        = (up   ? -1 : down  ? 1 : 0);
10990
10991   if (!player->active || tape.pausing)
10992     return 0;
10993
10994   if (player_action)
10995   {
10996     if (button1)
10997       SnapField(player, dx, dy);
10998     else
10999     {
11000       if (button2)
11001         DropElement(player);
11002
11003       MovePlayer(player, dx, dy);
11004     }
11005
11006     CheckSingleStepMode(player);
11007
11008     SetPlayerWaiting(player, FALSE);
11009
11010     return player_action;
11011   }
11012   else
11013   {
11014     /* no actions for this player (no input at player's configured device) */
11015
11016     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11017     SnapField(player, 0, 0);
11018     CheckGravityMovementWhenNotMoving(player);
11019
11020     if (player->MovPos == 0)
11021       SetPlayerWaiting(player, TRUE);
11022
11023     if (player->MovPos == 0)    /* needed for tape.playing */
11024       player->is_moving = FALSE;
11025
11026     player->is_dropping = FALSE;
11027     player->is_dropping_pressed = FALSE;
11028     player->drop_pressed_delay = 0;
11029
11030     CheckSingleStepMode(player);
11031
11032     return 0;
11033   }
11034 }
11035
11036 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11037                                          byte *tape_action)
11038 {
11039   if (!tape.use_mouse)
11040     return;
11041
11042   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11043   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11044   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11045 }
11046
11047 static void SetTapeActionFromMouseAction(byte *tape_action,
11048                                          struct MouseActionInfo *mouse_action)
11049 {
11050   if (!tape.use_mouse)
11051     return;
11052
11053   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11054   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11055   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11056 }
11057
11058 static void CheckLevelTime()
11059 {
11060   int i;
11061
11062   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11063   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11064   {
11065     if (level.native_em_level->lev->home == 0)  /* all players at home */
11066     {
11067       PlayerWins(local_player);
11068
11069       AllPlayersGone = TRUE;
11070
11071       level.native_em_level->lev->home = -1;
11072     }
11073
11074     if (level.native_em_level->ply[0]->alive == 0 &&
11075         level.native_em_level->ply[1]->alive == 0 &&
11076         level.native_em_level->ply[2]->alive == 0 &&
11077         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11078       AllPlayersGone = TRUE;
11079   }
11080   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11081   {
11082     if (game_sp.LevelSolved &&
11083         !game_sp.GameOver)                              /* game won */
11084     {
11085       PlayerWins(local_player);
11086
11087       game_sp.GameOver = TRUE;
11088
11089       AllPlayersGone = TRUE;
11090     }
11091
11092     if (game_sp.GameOver)                               /* game lost */
11093       AllPlayersGone = TRUE;
11094   }
11095   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11096   {
11097     if (game_mm.level_solved &&
11098         !game_mm.game_over)                             /* game won */
11099     {
11100       PlayerWins(local_player);
11101
11102       game_mm.game_over = TRUE;
11103
11104       AllPlayersGone = TRUE;
11105     }
11106
11107     if (game_mm.game_over)                              /* game lost */
11108       AllPlayersGone = TRUE;
11109   }
11110
11111   if (TimeFrames >= FRAMES_PER_SECOND)
11112   {
11113     TimeFrames = 0;
11114     TapeTime++;
11115
11116     for (i = 0; i < MAX_PLAYERS; i++)
11117     {
11118       struct PlayerInfo *player = &stored_player[i];
11119
11120       if (SHIELD_ON(player))
11121       {
11122         player->shield_normal_time_left--;
11123
11124         if (player->shield_deadly_time_left > 0)
11125           player->shield_deadly_time_left--;
11126       }
11127     }
11128
11129     if (!local_player->LevelSolved && !level.use_step_counter)
11130     {
11131       TimePlayed++;
11132
11133       if (TimeLeft > 0)
11134       {
11135         TimeLeft--;
11136
11137         if (TimeLeft <= 10 && setup.time_limit)
11138           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11139
11140         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11141            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11142
11143         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11144
11145         if (!TimeLeft && setup.time_limit)
11146         {
11147           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11148             level.native_em_level->lev->killed_out_of_time = TRUE;
11149           else
11150             for (i = 0; i < MAX_PLAYERS; i++)
11151               KillPlayer(&stored_player[i]);
11152         }
11153       }
11154       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11155       {
11156         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11157       }
11158
11159       level.native_em_level->lev->time =
11160         (game.no_time_limit ? TimePlayed : TimeLeft);
11161     }
11162
11163     if (tape.recording || tape.playing)
11164       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11165   }
11166
11167   if (tape.recording || tape.playing)
11168     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11169
11170   UpdateAndDisplayGameControlValues();
11171 }
11172
11173 void AdvanceFrameAndPlayerCounters(int player_nr)
11174 {
11175   int i;
11176
11177   /* advance frame counters (global frame counter and time frame counter) */
11178   FrameCounter++;
11179   TimeFrames++;
11180
11181   /* advance player counters (counters for move delay, move animation etc.) */
11182   for (i = 0; i < MAX_PLAYERS; i++)
11183   {
11184     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11185     int move_delay_value = stored_player[i].move_delay_value;
11186     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11187
11188     if (!advance_player_counters)       /* not all players may be affected */
11189       continue;
11190
11191     if (move_frames == 0)       /* less than one move per game frame */
11192     {
11193       int stepsize = TILEX / move_delay_value;
11194       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11195       int count = (stored_player[i].is_moving ?
11196                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11197
11198       if (count % delay == 0)
11199         move_frames = 1;
11200     }
11201
11202     stored_player[i].Frame += move_frames;
11203
11204     if (stored_player[i].MovPos != 0)
11205       stored_player[i].StepFrame += move_frames;
11206
11207     if (stored_player[i].move_delay > 0)
11208       stored_player[i].move_delay--;
11209
11210     /* due to bugs in previous versions, counter must count up, not down */
11211     if (stored_player[i].push_delay != -1)
11212       stored_player[i].push_delay++;
11213
11214     if (stored_player[i].drop_delay > 0)
11215       stored_player[i].drop_delay--;
11216
11217     if (stored_player[i].is_dropping_pressed)
11218       stored_player[i].drop_pressed_delay++;
11219   }
11220 }
11221
11222 void StartGameActions(boolean init_network_game, boolean record_tape,
11223                       int random_seed)
11224 {
11225   unsigned int new_random_seed = InitRND(random_seed);
11226
11227   if (record_tape)
11228     TapeStartRecording(new_random_seed);
11229
11230   if (init_network_game)
11231   {
11232     SendToServer_LevelFile();
11233     SendToServer_StartPlaying();
11234
11235     return;
11236   }
11237
11238   InitGame();
11239 }
11240
11241 void GameActionsExt()
11242 {
11243 #if 0
11244   static unsigned int game_frame_delay = 0;
11245 #endif
11246   unsigned int game_frame_delay_value;
11247   byte *recorded_player_action;
11248   byte summarized_player_action = 0;
11249   byte tape_action[MAX_PLAYERS];
11250   int i;
11251
11252   /* detect endless loops, caused by custom element programming */
11253   if (recursion_loop_detected && recursion_loop_depth == 0)
11254   {
11255     char *message = getStringCat3("Internal Error! Element ",
11256                                   EL_NAME(recursion_loop_element),
11257                                   " caused endless loop! Quit the game?");
11258
11259     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11260           EL_NAME(recursion_loop_element));
11261
11262     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11263
11264     recursion_loop_detected = FALSE;    /* if game should be continued */
11265
11266     free(message);
11267
11268     return;
11269   }
11270
11271   if (game.restart_level)
11272     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11273
11274   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11275   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11276   {
11277     if (level.native_em_level->lev->home == 0)  /* all players at home */
11278     {
11279       PlayerWins(local_player);
11280
11281       AllPlayersGone = TRUE;
11282
11283       level.native_em_level->lev->home = -1;
11284     }
11285
11286     if (level.native_em_level->ply[0]->alive == 0 &&
11287         level.native_em_level->ply[1]->alive == 0 &&
11288         level.native_em_level->ply[2]->alive == 0 &&
11289         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11290       AllPlayersGone = TRUE;
11291   }
11292   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11293   {
11294     if (game_sp.LevelSolved &&
11295         !game_sp.GameOver)                              /* game won */
11296     {
11297       PlayerWins(local_player);
11298
11299       game_sp.GameOver = TRUE;
11300
11301       AllPlayersGone = TRUE;
11302     }
11303
11304     if (game_sp.GameOver)                               /* game lost */
11305       AllPlayersGone = TRUE;
11306   }
11307   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11308   {
11309     if (game_mm.level_solved &&
11310         !game_mm.game_over)                             /* game won */
11311     {
11312       PlayerWins(local_player);
11313
11314       game_mm.game_over = TRUE;
11315
11316       AllPlayersGone = TRUE;
11317     }
11318
11319     if (game_mm.game_over)                              /* game lost */
11320       AllPlayersGone = TRUE;
11321   }
11322
11323   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11324     GameWon();
11325
11326   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11327     TapeStop();
11328
11329   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11330     return;
11331
11332   game_frame_delay_value =
11333     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11334
11335   if (tape.playing && tape.warp_forward && !tape.pausing)
11336     game_frame_delay_value = 0;
11337
11338   SetVideoFrameDelay(game_frame_delay_value);
11339
11340 #if 0
11341 #if 0
11342   /* ---------- main game synchronization point ---------- */
11343
11344   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11345
11346   printf("::: skip == %d\n", skip);
11347
11348 #else
11349   /* ---------- main game synchronization point ---------- */
11350
11351   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11352 #endif
11353 #endif
11354
11355   if (network_playing && !network_player_action_received)
11356   {
11357     /* try to get network player actions in time */
11358
11359     /* last chance to get network player actions without main loop delay */
11360     HandleNetworking();
11361
11362     /* game was quit by network peer */
11363     if (game_status != GAME_MODE_PLAYING)
11364       return;
11365
11366     if (!network_player_action_received)
11367       return;           /* failed to get network player actions in time */
11368
11369     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11370   }
11371
11372   if (tape.pausing)
11373     return;
11374
11375   /* at this point we know that we really continue executing the game */
11376
11377   network_player_action_received = FALSE;
11378
11379   /* when playing tape, read previously recorded player input from tape data */
11380   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11381
11382   local_player->effective_mouse_action = local_player->mouse_action;
11383
11384   if (recorded_player_action != NULL)
11385     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11386                                  recorded_player_action);
11387
11388   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11389   if (tape.pausing)
11390     return;
11391
11392   if (tape.set_centered_player)
11393   {
11394     game.centered_player_nr_next = tape.centered_player_nr_next;
11395     game.set_centered_player = TRUE;
11396   }
11397
11398   for (i = 0; i < MAX_PLAYERS; i++)
11399   {
11400     summarized_player_action |= stored_player[i].action;
11401
11402     if (!network_playing && (game.team_mode || tape.playing))
11403       stored_player[i].effective_action = stored_player[i].action;
11404   }
11405
11406   if (network_playing)
11407     SendToServer_MovePlayer(summarized_player_action);
11408
11409   // summarize all actions at local players mapped input device position
11410   // (this allows using different input devices in single player mode)
11411   if (!network.enabled && !game.team_mode)
11412     stored_player[map_player_action[local_player->index_nr]].effective_action =
11413       summarized_player_action;
11414
11415   if (tape.recording &&
11416       setup.team_mode &&
11417       setup.input_on_focus &&
11418       game.centered_player_nr != -1)
11419   {
11420     for (i = 0; i < MAX_PLAYERS; i++)
11421       stored_player[i].effective_action =
11422         (i == game.centered_player_nr ? summarized_player_action : 0);
11423   }
11424
11425   if (recorded_player_action != NULL)
11426     for (i = 0; i < MAX_PLAYERS; i++)
11427       stored_player[i].effective_action = recorded_player_action[i];
11428
11429   for (i = 0; i < MAX_PLAYERS; i++)
11430   {
11431     tape_action[i] = stored_player[i].effective_action;
11432
11433     /* (this may happen in the RND game engine if a player was not present on
11434        the playfield on level start, but appeared later from a custom element */
11435     if (setup.team_mode &&
11436         tape.recording &&
11437         tape_action[i] &&
11438         !tape.player_participates[i])
11439       tape.player_participates[i] = TRUE;
11440   }
11441
11442   SetTapeActionFromMouseAction(tape_action,
11443                                &local_player->effective_mouse_action);
11444
11445   /* only record actions from input devices, but not programmed actions */
11446   if (tape.recording)
11447     TapeRecordAction(tape_action);
11448
11449 #if USE_NEW_PLAYER_ASSIGNMENTS
11450   // !!! also map player actions in single player mode !!!
11451   // if (game.team_mode)
11452   if (1)
11453   {
11454     byte mapped_action[MAX_PLAYERS];
11455
11456 #if DEBUG_PLAYER_ACTIONS
11457     printf(":::");
11458     for (i = 0; i < MAX_PLAYERS; i++)
11459       printf(" %d, ", stored_player[i].effective_action);
11460 #endif
11461
11462     for (i = 0; i < MAX_PLAYERS; i++)
11463       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11464
11465     for (i = 0; i < MAX_PLAYERS; i++)
11466       stored_player[i].effective_action = mapped_action[i];
11467
11468 #if DEBUG_PLAYER_ACTIONS
11469     printf(" =>");
11470     for (i = 0; i < MAX_PLAYERS; i++)
11471       printf(" %d, ", stored_player[i].effective_action);
11472     printf("\n");
11473 #endif
11474   }
11475 #if DEBUG_PLAYER_ACTIONS
11476   else
11477   {
11478     printf(":::");
11479     for (i = 0; i < MAX_PLAYERS; i++)
11480       printf(" %d, ", stored_player[i].effective_action);
11481     printf("\n");
11482   }
11483 #endif
11484 #endif
11485
11486   for (i = 0; i < MAX_PLAYERS; i++)
11487   {
11488     // allow engine snapshot in case of changed movement attempt
11489     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11490         (stored_player[i].effective_action & KEY_MOTION))
11491       game.snapshot.changed_action = TRUE;
11492
11493     // allow engine snapshot in case of snapping/dropping attempt
11494     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11495         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11496       game.snapshot.changed_action = TRUE;
11497
11498     game.snapshot.last_action[i] = stored_player[i].effective_action;
11499   }
11500
11501   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11502   {
11503     GameActions_EM_Main();
11504   }
11505   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11506   {
11507     GameActions_SP_Main();
11508   }
11509   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11510   {
11511     GameActions_MM_Main();
11512   }
11513   else
11514   {
11515     GameActions_RND_Main();
11516   }
11517
11518   BlitScreenToBitmap(backbuffer);
11519
11520   CheckLevelTime();
11521
11522   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11523
11524   if (global.show_frames_per_second)
11525   {
11526     static unsigned int fps_counter = 0;
11527     static int fps_frames = 0;
11528     unsigned int fps_delay_ms = Counter() - fps_counter;
11529
11530     fps_frames++;
11531
11532     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11533     {
11534       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11535
11536       fps_frames = 0;
11537       fps_counter = Counter();
11538
11539       /* always draw FPS to screen after FPS value was updated */
11540       redraw_mask |= REDRAW_FPS;
11541     }
11542
11543     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11544     if (GetDrawDeactivationMask() == REDRAW_NONE)
11545       redraw_mask |= REDRAW_FPS;
11546   }
11547 }
11548
11549 static void GameActions_CheckSaveEngineSnapshot()
11550 {
11551   if (!game.snapshot.save_snapshot)
11552     return;
11553
11554   // clear flag for saving snapshot _before_ saving snapshot
11555   game.snapshot.save_snapshot = FALSE;
11556
11557   SaveEngineSnapshotToList();
11558 }
11559
11560 void GameActions()
11561 {
11562   GameActionsExt();
11563
11564   GameActions_CheckSaveEngineSnapshot();
11565 }
11566
11567 void GameActions_EM_Main()
11568 {
11569   byte effective_action[MAX_PLAYERS];
11570   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11571   int i;
11572
11573   for (i = 0; i < MAX_PLAYERS; i++)
11574     effective_action[i] = stored_player[i].effective_action;
11575
11576   GameActions_EM(effective_action, warp_mode);
11577 }
11578
11579 void GameActions_SP_Main()
11580 {
11581   byte effective_action[MAX_PLAYERS];
11582   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11583   int i;
11584
11585   for (i = 0; i < MAX_PLAYERS; i++)
11586     effective_action[i] = stored_player[i].effective_action;
11587
11588   GameActions_SP(effective_action, warp_mode);
11589
11590   for (i = 0; i < MAX_PLAYERS; i++)
11591   {
11592     if (stored_player[i].force_dropping)
11593       stored_player[i].action |= KEY_BUTTON_DROP;
11594
11595     stored_player[i].force_dropping = FALSE;
11596   }
11597 }
11598
11599 void GameActions_MM_Main()
11600 {
11601   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11602
11603   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11604 }
11605
11606 void GameActions_RND_Main()
11607 {
11608   GameActions_RND();
11609 }
11610
11611 void GameActions_RND()
11612 {
11613   int magic_wall_x = 0, magic_wall_y = 0;
11614   int i, x, y, element, graphic, last_gfx_frame;
11615
11616   InitPlayfieldScanModeVars();
11617
11618   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11619   {
11620     SCAN_PLAYFIELD(x, y)
11621     {
11622       ChangeCount[x][y] = 0;
11623       ChangeEvent[x][y] = -1;
11624     }
11625   }
11626
11627   if (game.set_centered_player)
11628   {
11629     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11630
11631     /* switching to "all players" only possible if all players fit to screen */
11632     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11633     {
11634       game.centered_player_nr_next = game.centered_player_nr;
11635       game.set_centered_player = FALSE;
11636     }
11637
11638     /* do not switch focus to non-existing (or non-active) player */
11639     if (game.centered_player_nr_next >= 0 &&
11640         !stored_player[game.centered_player_nr_next].active)
11641     {
11642       game.centered_player_nr_next = game.centered_player_nr;
11643       game.set_centered_player = FALSE;
11644     }
11645   }
11646
11647   if (game.set_centered_player &&
11648       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11649   {
11650     int sx, sy;
11651
11652     if (game.centered_player_nr_next == -1)
11653     {
11654       setScreenCenteredToAllPlayers(&sx, &sy);
11655     }
11656     else
11657     {
11658       sx = stored_player[game.centered_player_nr_next].jx;
11659       sy = stored_player[game.centered_player_nr_next].jy;
11660     }
11661
11662     game.centered_player_nr = game.centered_player_nr_next;
11663     game.set_centered_player = FALSE;
11664
11665     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11666     DrawGameDoorValues();
11667   }
11668
11669   for (i = 0; i < MAX_PLAYERS; i++)
11670   {
11671     int actual_player_action = stored_player[i].effective_action;
11672
11673 #if 1
11674     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11675        - rnd_equinox_tetrachloride 048
11676        - rnd_equinox_tetrachloride_ii 096
11677        - rnd_emanuel_schmieg 002
11678        - doctor_sloan_ww 001, 020
11679     */
11680     if (stored_player[i].MovPos == 0)
11681       CheckGravityMovement(&stored_player[i]);
11682 #endif
11683
11684     /* overwrite programmed action with tape action */
11685     if (stored_player[i].programmed_action)
11686       actual_player_action = stored_player[i].programmed_action;
11687
11688     PlayerActions(&stored_player[i], actual_player_action);
11689
11690     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11691   }
11692
11693   ScrollScreen(NULL, SCROLL_GO_ON);
11694
11695   /* for backwards compatibility, the following code emulates a fixed bug that
11696      occured when pushing elements (causing elements that just made their last
11697      pushing step to already (if possible) make their first falling step in the
11698      same game frame, which is bad); this code is also needed to use the famous
11699      "spring push bug" which is used in older levels and might be wanted to be
11700      used also in newer levels, but in this case the buggy pushing code is only
11701      affecting the "spring" element and no other elements */
11702
11703   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11704   {
11705     for (i = 0; i < MAX_PLAYERS; i++)
11706     {
11707       struct PlayerInfo *player = &stored_player[i];
11708       int x = player->jx;
11709       int y = player->jy;
11710
11711       if (player->active && player->is_pushing && player->is_moving &&
11712           IS_MOVING(x, y) &&
11713           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11714            Feld[x][y] == EL_SPRING))
11715       {
11716         ContinueMoving(x, y);
11717
11718         /* continue moving after pushing (this is actually a bug) */
11719         if (!IS_MOVING(x, y))
11720           Stop[x][y] = FALSE;
11721       }
11722     }
11723   }
11724
11725   SCAN_PLAYFIELD(x, y)
11726   {
11727     ChangeCount[x][y] = 0;
11728     ChangeEvent[x][y] = -1;
11729
11730     /* this must be handled before main playfield loop */
11731     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11732     {
11733       MovDelay[x][y]--;
11734       if (MovDelay[x][y] <= 0)
11735         RemoveField(x, y);
11736     }
11737
11738     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11739     {
11740       MovDelay[x][y]--;
11741       if (MovDelay[x][y] <= 0)
11742       {
11743         RemoveField(x, y);
11744         TEST_DrawLevelField(x, y);
11745
11746         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11747       }
11748     }
11749
11750 #if DEBUG
11751     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11752     {
11753       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11754       printf("GameActions(): This should never happen!\n");
11755
11756       ChangePage[x][y] = -1;
11757     }
11758 #endif
11759
11760     Stop[x][y] = FALSE;
11761     if (WasJustMoving[x][y] > 0)
11762       WasJustMoving[x][y]--;
11763     if (WasJustFalling[x][y] > 0)
11764       WasJustFalling[x][y]--;
11765     if (CheckCollision[x][y] > 0)
11766       CheckCollision[x][y]--;
11767     if (CheckImpact[x][y] > 0)
11768       CheckImpact[x][y]--;
11769
11770     GfxFrame[x][y]++;
11771
11772     /* reset finished pushing action (not done in ContinueMoving() to allow
11773        continuous pushing animation for elements with zero push delay) */
11774     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11775     {
11776       ResetGfxAnimation(x, y);
11777       TEST_DrawLevelField(x, y);
11778     }
11779
11780 #if DEBUG
11781     if (IS_BLOCKED(x, y))
11782     {
11783       int oldx, oldy;
11784
11785       Blocked2Moving(x, y, &oldx, &oldy);
11786       if (!IS_MOVING(oldx, oldy))
11787       {
11788         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11789         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11790         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11791         printf("GameActions(): This should never happen!\n");
11792       }
11793     }
11794 #endif
11795   }
11796
11797   SCAN_PLAYFIELD(x, y)
11798   {
11799     element = Feld[x][y];
11800     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11801     last_gfx_frame = GfxFrame[x][y];
11802
11803     ResetGfxFrame(x, y);
11804
11805     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11806       DrawLevelGraphicAnimation(x, y, graphic);
11807
11808     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11809         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11810       ResetRandomAnimationValue(x, y);
11811
11812     SetRandomAnimationValue(x, y);
11813
11814     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11815
11816     if (IS_INACTIVE(element))
11817     {
11818       if (IS_ANIMATED(graphic))
11819         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11820
11821       continue;
11822     }
11823
11824     /* this may take place after moving, so 'element' may have changed */
11825     if (IS_CHANGING(x, y) &&
11826         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11827     {
11828       int page = element_info[element].event_page_nr[CE_DELAY];
11829
11830       HandleElementChange(x, y, page);
11831
11832       element = Feld[x][y];
11833       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11834     }
11835
11836     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11837     {
11838       StartMoving(x, y);
11839
11840       element = Feld[x][y];
11841       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11842
11843       if (IS_ANIMATED(graphic) &&
11844           !IS_MOVING(x, y) &&
11845           !Stop[x][y])
11846         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11847
11848       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11849         TEST_DrawTwinkleOnField(x, y);
11850     }
11851     else if (element == EL_ACID)
11852     {
11853       if (!Stop[x][y])
11854         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11855     }
11856     else if ((element == EL_EXIT_OPEN ||
11857               element == EL_EM_EXIT_OPEN ||
11858               element == EL_SP_EXIT_OPEN ||
11859               element == EL_STEEL_EXIT_OPEN ||
11860               element == EL_EM_STEEL_EXIT_OPEN ||
11861               element == EL_SP_TERMINAL ||
11862               element == EL_SP_TERMINAL_ACTIVE ||
11863               element == EL_EXTRA_TIME ||
11864               element == EL_SHIELD_NORMAL ||
11865               element == EL_SHIELD_DEADLY) &&
11866              IS_ANIMATED(graphic))
11867       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11868     else if (IS_MOVING(x, y))
11869       ContinueMoving(x, y);
11870     else if (IS_ACTIVE_BOMB(element))
11871       CheckDynamite(x, y);
11872     else if (element == EL_AMOEBA_GROWING)
11873       AmoebeWaechst(x, y);
11874     else if (element == EL_AMOEBA_SHRINKING)
11875       AmoebaDisappearing(x, y);
11876
11877 #if !USE_NEW_AMOEBA_CODE
11878     else if (IS_AMOEBALIVE(element))
11879       AmoebeAbleger(x, y);
11880 #endif
11881
11882     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11883       Life(x, y);
11884     else if (element == EL_EXIT_CLOSED)
11885       CheckExit(x, y);
11886     else if (element == EL_EM_EXIT_CLOSED)
11887       CheckExitEM(x, y);
11888     else if (element == EL_STEEL_EXIT_CLOSED)
11889       CheckExitSteel(x, y);
11890     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11891       CheckExitSteelEM(x, y);
11892     else if (element == EL_SP_EXIT_CLOSED)
11893       CheckExitSP(x, y);
11894     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11895              element == EL_EXPANDABLE_STEELWALL_GROWING)
11896       MauerWaechst(x, y);
11897     else if (element == EL_EXPANDABLE_WALL ||
11898              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11899              element == EL_EXPANDABLE_WALL_VERTICAL ||
11900              element == EL_EXPANDABLE_WALL_ANY ||
11901              element == EL_BD_EXPANDABLE_WALL)
11902       MauerAbleger(x, y);
11903     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11904              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11905              element == EL_EXPANDABLE_STEELWALL_ANY)
11906       MauerAblegerStahl(x, y);
11907     else if (element == EL_FLAMES)
11908       CheckForDragon(x, y);
11909     else if (element == EL_EXPLOSION)
11910       ; /* drawing of correct explosion animation is handled separately */
11911     else if (element == EL_ELEMENT_SNAPPING ||
11912              element == EL_DIAGONAL_SHRINKING ||
11913              element == EL_DIAGONAL_GROWING)
11914     {
11915       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11916
11917       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11918     }
11919     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11920       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11921
11922     if (IS_BELT_ACTIVE(element))
11923       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11924
11925     if (game.magic_wall_active)
11926     {
11927       int jx = local_player->jx, jy = local_player->jy;
11928
11929       /* play the element sound at the position nearest to the player */
11930       if ((element == EL_MAGIC_WALL_FULL ||
11931            element == EL_MAGIC_WALL_ACTIVE ||
11932            element == EL_MAGIC_WALL_EMPTYING ||
11933            element == EL_BD_MAGIC_WALL_FULL ||
11934            element == EL_BD_MAGIC_WALL_ACTIVE ||
11935            element == EL_BD_MAGIC_WALL_EMPTYING ||
11936            element == EL_DC_MAGIC_WALL_FULL ||
11937            element == EL_DC_MAGIC_WALL_ACTIVE ||
11938            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11939           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11940       {
11941         magic_wall_x = x;
11942         magic_wall_y = y;
11943       }
11944     }
11945   }
11946
11947 #if USE_NEW_AMOEBA_CODE
11948   /* new experimental amoeba growth stuff */
11949   if (!(FrameCounter % 8))
11950   {
11951     static unsigned int random = 1684108901;
11952
11953     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11954     {
11955       x = RND(lev_fieldx);
11956       y = RND(lev_fieldy);
11957       element = Feld[x][y];
11958
11959       if (!IS_PLAYER(x,y) &&
11960           (element == EL_EMPTY ||
11961            CAN_GROW_INTO(element) ||
11962            element == EL_QUICKSAND_EMPTY ||
11963            element == EL_QUICKSAND_FAST_EMPTY ||
11964            element == EL_ACID_SPLASH_LEFT ||
11965            element == EL_ACID_SPLASH_RIGHT))
11966       {
11967         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11968             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11969             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11970             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11971           Feld[x][y] = EL_AMOEBA_DROP;
11972       }
11973
11974       random = random * 129 + 1;
11975     }
11976   }
11977 #endif
11978
11979   game.explosions_delayed = FALSE;
11980
11981   SCAN_PLAYFIELD(x, y)
11982   {
11983     element = Feld[x][y];
11984
11985     if (ExplodeField[x][y])
11986       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11987     else if (element == EL_EXPLOSION)
11988       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11989
11990     ExplodeField[x][y] = EX_TYPE_NONE;
11991   }
11992
11993   game.explosions_delayed = TRUE;
11994
11995   if (game.magic_wall_active)
11996   {
11997     if (!(game.magic_wall_time_left % 4))
11998     {
11999       int element = Feld[magic_wall_x][magic_wall_y];
12000
12001       if (element == EL_BD_MAGIC_WALL_FULL ||
12002           element == EL_BD_MAGIC_WALL_ACTIVE ||
12003           element == EL_BD_MAGIC_WALL_EMPTYING)
12004         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12005       else if (element == EL_DC_MAGIC_WALL_FULL ||
12006                element == EL_DC_MAGIC_WALL_ACTIVE ||
12007                element == EL_DC_MAGIC_WALL_EMPTYING)
12008         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12009       else
12010         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12011     }
12012
12013     if (game.magic_wall_time_left > 0)
12014     {
12015       game.magic_wall_time_left--;
12016
12017       if (!game.magic_wall_time_left)
12018       {
12019         SCAN_PLAYFIELD(x, y)
12020         {
12021           element = Feld[x][y];
12022
12023           if (element == EL_MAGIC_WALL_ACTIVE ||
12024               element == EL_MAGIC_WALL_FULL)
12025           {
12026             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12027             TEST_DrawLevelField(x, y);
12028           }
12029           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12030                    element == EL_BD_MAGIC_WALL_FULL)
12031           {
12032             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12033             TEST_DrawLevelField(x, y);
12034           }
12035           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12036                    element == EL_DC_MAGIC_WALL_FULL)
12037           {
12038             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12039             TEST_DrawLevelField(x, y);
12040           }
12041         }
12042
12043         game.magic_wall_active = FALSE;
12044       }
12045     }
12046   }
12047
12048   if (game.light_time_left > 0)
12049   {
12050     game.light_time_left--;
12051
12052     if (game.light_time_left == 0)
12053       RedrawAllLightSwitchesAndInvisibleElements();
12054   }
12055
12056   if (game.timegate_time_left > 0)
12057   {
12058     game.timegate_time_left--;
12059
12060     if (game.timegate_time_left == 0)
12061       CloseAllOpenTimegates();
12062   }
12063
12064   if (game.lenses_time_left > 0)
12065   {
12066     game.lenses_time_left--;
12067
12068     if (game.lenses_time_left == 0)
12069       RedrawAllInvisibleElementsForLenses();
12070   }
12071
12072   if (game.magnify_time_left > 0)
12073   {
12074     game.magnify_time_left--;
12075
12076     if (game.magnify_time_left == 0)
12077       RedrawAllInvisibleElementsForMagnifier();
12078   }
12079
12080   for (i = 0; i < MAX_PLAYERS; i++)
12081   {
12082     struct PlayerInfo *player = &stored_player[i];
12083
12084     if (SHIELD_ON(player))
12085     {
12086       if (player->shield_deadly_time_left)
12087         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12088       else if (player->shield_normal_time_left)
12089         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12090     }
12091   }
12092
12093 #if USE_DELAYED_GFX_REDRAW
12094   SCAN_PLAYFIELD(x, y)
12095   {
12096     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12097     {
12098       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12099          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12100
12101       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12102         DrawLevelField(x, y);
12103
12104       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12105         DrawLevelFieldCrumbled(x, y);
12106
12107       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12108         DrawLevelFieldCrumbledNeighbours(x, y);
12109
12110       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12111         DrawTwinkleOnField(x, y);
12112     }
12113
12114     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12115   }
12116 #endif
12117
12118   DrawAllPlayers();
12119   PlayAllPlayersSound();
12120
12121   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12122   {
12123     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12124
12125     local_player->show_envelope = 0;
12126   }
12127
12128   /* use random number generator in every frame to make it less predictable */
12129   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12130     RND(1);
12131 }
12132
12133 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12134 {
12135   int min_x = x, min_y = y, max_x = x, max_y = y;
12136   int i;
12137
12138   for (i = 0; i < MAX_PLAYERS; i++)
12139   {
12140     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12141
12142     if (!stored_player[i].active || &stored_player[i] == player)
12143       continue;
12144
12145     min_x = MIN(min_x, jx);
12146     min_y = MIN(min_y, jy);
12147     max_x = MAX(max_x, jx);
12148     max_y = MAX(max_y, jy);
12149   }
12150
12151   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12152 }
12153
12154 static boolean AllPlayersInVisibleScreen()
12155 {
12156   int i;
12157
12158   for (i = 0; i < MAX_PLAYERS; i++)
12159   {
12160     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12161
12162     if (!stored_player[i].active)
12163       continue;
12164
12165     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12166       return FALSE;
12167   }
12168
12169   return TRUE;
12170 }
12171
12172 void ScrollLevel(int dx, int dy)
12173 {
12174   int scroll_offset = 2 * TILEX_VAR;
12175   int x, y;
12176
12177   BlitBitmap(drawto_field, drawto_field,
12178              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12179              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12180              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12181              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12182              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12183              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12184
12185   if (dx != 0)
12186   {
12187     x = (dx == 1 ? BX1 : BX2);
12188     for (y = BY1; y <= BY2; y++)
12189       DrawScreenField(x, y);
12190   }
12191
12192   if (dy != 0)
12193   {
12194     y = (dy == 1 ? BY1 : BY2);
12195     for (x = BX1; x <= BX2; x++)
12196       DrawScreenField(x, y);
12197   }
12198
12199   redraw_mask |= REDRAW_FIELD;
12200 }
12201
12202 static boolean canFallDown(struct PlayerInfo *player)
12203 {
12204   int jx = player->jx, jy = player->jy;
12205
12206   return (IN_LEV_FIELD(jx, jy + 1) &&
12207           (IS_FREE(jx, jy + 1) ||
12208            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12209           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12210           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12211 }
12212
12213 static boolean canPassField(int x, int y, int move_dir)
12214 {
12215   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12216   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12217   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12218   int nextx = x + dx;
12219   int nexty = y + dy;
12220   int element = Feld[x][y];
12221
12222   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12223           !CAN_MOVE(element) &&
12224           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12225           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12226           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12227 }
12228
12229 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12230 {
12231   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12232   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12233   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12234   int newx = x + dx;
12235   int newy = y + dy;
12236
12237   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12238           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12239           (IS_DIGGABLE(Feld[newx][newy]) ||
12240            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12241            canPassField(newx, newy, move_dir)));
12242 }
12243
12244 static void CheckGravityMovement(struct PlayerInfo *player)
12245 {
12246   if (player->gravity && !player->programmed_action)
12247   {
12248     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12249     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12250     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12251     int jx = player->jx, jy = player->jy;
12252     boolean player_is_moving_to_valid_field =
12253       (!player_is_snapping &&
12254        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12255         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12256     boolean player_can_fall_down = canFallDown(player);
12257
12258     if (player_can_fall_down &&
12259         !player_is_moving_to_valid_field)
12260       player->programmed_action = MV_DOWN;
12261   }
12262 }
12263
12264 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12265 {
12266   return CheckGravityMovement(player);
12267
12268   if (player->gravity && !player->programmed_action)
12269   {
12270     int jx = player->jx, jy = player->jy;
12271     boolean field_under_player_is_free =
12272       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12273     boolean player_is_standing_on_valid_field =
12274       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12275        (IS_WALKABLE(Feld[jx][jy]) &&
12276         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12277
12278     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12279       player->programmed_action = MV_DOWN;
12280   }
12281 }
12282
12283 /*
12284   MovePlayerOneStep()
12285   -----------------------------------------------------------------------------
12286   dx, dy:               direction (non-diagonal) to try to move the player to
12287   real_dx, real_dy:     direction as read from input device (can be diagonal)
12288 */
12289
12290 boolean MovePlayerOneStep(struct PlayerInfo *player,
12291                           int dx, int dy, int real_dx, int real_dy)
12292 {
12293   int jx = player->jx, jy = player->jy;
12294   int new_jx = jx + dx, new_jy = jy + dy;
12295   int can_move;
12296   boolean player_can_move = !player->cannot_move;
12297
12298   if (!player->active || (!dx && !dy))
12299     return MP_NO_ACTION;
12300
12301   player->MovDir = (dx < 0 ? MV_LEFT :
12302                     dx > 0 ? MV_RIGHT :
12303                     dy < 0 ? MV_UP :
12304                     dy > 0 ? MV_DOWN :  MV_NONE);
12305
12306   if (!IN_LEV_FIELD(new_jx, new_jy))
12307     return MP_NO_ACTION;
12308
12309   if (!player_can_move)
12310   {
12311     if (player->MovPos == 0)
12312     {
12313       player->is_moving = FALSE;
12314       player->is_digging = FALSE;
12315       player->is_collecting = FALSE;
12316       player->is_snapping = FALSE;
12317       player->is_pushing = FALSE;
12318     }
12319   }
12320
12321   if (!network.enabled && game.centered_player_nr == -1 &&
12322       !AllPlayersInSight(player, new_jx, new_jy))
12323     return MP_NO_ACTION;
12324
12325   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12326   if (can_move != MP_MOVING)
12327     return can_move;
12328
12329   /* check if DigField() has caused relocation of the player */
12330   if (player->jx != jx || player->jy != jy)
12331     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12332
12333   StorePlayer[jx][jy] = 0;
12334   player->last_jx = jx;
12335   player->last_jy = jy;
12336   player->jx = new_jx;
12337   player->jy = new_jy;
12338   StorePlayer[new_jx][new_jy] = player->element_nr;
12339
12340   if (player->move_delay_value_next != -1)
12341   {
12342     player->move_delay_value = player->move_delay_value_next;
12343     player->move_delay_value_next = -1;
12344   }
12345
12346   player->MovPos =
12347     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12348
12349   player->step_counter++;
12350
12351   PlayerVisit[jx][jy] = FrameCounter;
12352
12353   player->is_moving = TRUE;
12354
12355 #if 1
12356   /* should better be called in MovePlayer(), but this breaks some tapes */
12357   ScrollPlayer(player, SCROLL_INIT);
12358 #endif
12359
12360   return MP_MOVING;
12361 }
12362
12363 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12364 {
12365   int jx = player->jx, jy = player->jy;
12366   int old_jx = jx, old_jy = jy;
12367   int moved = MP_NO_ACTION;
12368
12369   if (!player->active)
12370     return FALSE;
12371
12372   if (!dx && !dy)
12373   {
12374     if (player->MovPos == 0)
12375     {
12376       player->is_moving = FALSE;
12377       player->is_digging = FALSE;
12378       player->is_collecting = FALSE;
12379       player->is_snapping = FALSE;
12380       player->is_pushing = FALSE;
12381     }
12382
12383     return FALSE;
12384   }
12385
12386   if (player->move_delay > 0)
12387     return FALSE;
12388
12389   player->move_delay = -1;              /* set to "uninitialized" value */
12390
12391   /* store if player is automatically moved to next field */
12392   player->is_auto_moving = (player->programmed_action != MV_NONE);
12393
12394   /* remove the last programmed player action */
12395   player->programmed_action = 0;
12396
12397   if (player->MovPos)
12398   {
12399     /* should only happen if pre-1.2 tape recordings are played */
12400     /* this is only for backward compatibility */
12401
12402     int original_move_delay_value = player->move_delay_value;
12403
12404 #if DEBUG
12405     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12406            tape.counter);
12407 #endif
12408
12409     /* scroll remaining steps with finest movement resolution */
12410     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12411
12412     while (player->MovPos)
12413     {
12414       ScrollPlayer(player, SCROLL_GO_ON);
12415       ScrollScreen(NULL, SCROLL_GO_ON);
12416
12417       AdvanceFrameAndPlayerCounters(player->index_nr);
12418
12419       DrawAllPlayers();
12420       BackToFront_WithFrameDelay(0);
12421     }
12422
12423     player->move_delay_value = original_move_delay_value;
12424   }
12425
12426   player->is_active = FALSE;
12427
12428   if (player->last_move_dir & MV_HORIZONTAL)
12429   {
12430     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12431       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12432   }
12433   else
12434   {
12435     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12436       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12437   }
12438
12439   if (!moved && !player->is_active)
12440   {
12441     player->is_moving = FALSE;
12442     player->is_digging = FALSE;
12443     player->is_collecting = FALSE;
12444     player->is_snapping = FALSE;
12445     player->is_pushing = FALSE;
12446   }
12447
12448   jx = player->jx;
12449   jy = player->jy;
12450
12451   if (moved & MP_MOVING && !ScreenMovPos &&
12452       (player->index_nr == game.centered_player_nr ||
12453        game.centered_player_nr == -1))
12454   {
12455     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12456     int offset = game.scroll_delay_value;
12457
12458     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12459     {
12460       /* actual player has left the screen -- scroll in that direction */
12461       if (jx != old_jx)         /* player has moved horizontally */
12462         scroll_x += (jx - old_jx);
12463       else                      /* player has moved vertically */
12464         scroll_y += (jy - old_jy);
12465     }
12466     else
12467     {
12468       if (jx != old_jx)         /* player has moved horizontally */
12469       {
12470         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12471             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12472           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12473
12474         /* don't scroll over playfield boundaries */
12475         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12476           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12477
12478         /* don't scroll more than one field at a time */
12479         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12480
12481         /* don't scroll against the player's moving direction */
12482         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12483             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12484           scroll_x = old_scroll_x;
12485       }
12486       else                      /* player has moved vertically */
12487       {
12488         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12489             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12490           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12491
12492         /* don't scroll over playfield boundaries */
12493         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12494           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12495
12496         /* don't scroll more than one field at a time */
12497         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12498
12499         /* don't scroll against the player's moving direction */
12500         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12501             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12502           scroll_y = old_scroll_y;
12503       }
12504     }
12505
12506     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12507     {
12508       if (!network.enabled && game.centered_player_nr == -1 &&
12509           !AllPlayersInVisibleScreen())
12510       {
12511         scroll_x = old_scroll_x;
12512         scroll_y = old_scroll_y;
12513       }
12514       else
12515       {
12516         ScrollScreen(player, SCROLL_INIT);
12517         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12518       }
12519     }
12520   }
12521
12522   player->StepFrame = 0;
12523
12524   if (moved & MP_MOVING)
12525   {
12526     if (old_jx != jx && old_jy == jy)
12527       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12528     else if (old_jx == jx && old_jy != jy)
12529       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12530
12531     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12532
12533     player->last_move_dir = player->MovDir;
12534     player->is_moving = TRUE;
12535     player->is_snapping = FALSE;
12536     player->is_switching = FALSE;
12537     player->is_dropping = FALSE;
12538     player->is_dropping_pressed = FALSE;
12539     player->drop_pressed_delay = 0;
12540
12541 #if 0
12542     /* should better be called here than above, but this breaks some tapes */
12543     ScrollPlayer(player, SCROLL_INIT);
12544 #endif
12545   }
12546   else
12547   {
12548     CheckGravityMovementWhenNotMoving(player);
12549
12550     player->is_moving = FALSE;
12551
12552     /* at this point, the player is allowed to move, but cannot move right now
12553        (e.g. because of something blocking the way) -- ensure that the player
12554        is also allowed to move in the next frame (in old versions before 3.1.1,
12555        the player was forced to wait again for eight frames before next try) */
12556
12557     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12558       player->move_delay = 0;   /* allow direct movement in the next frame */
12559   }
12560
12561   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12562     player->move_delay = player->move_delay_value;
12563
12564   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12565   {
12566     TestIfPlayerTouchesBadThing(jx, jy);
12567     TestIfPlayerTouchesCustomElement(jx, jy);
12568   }
12569
12570   if (!player->active)
12571     RemovePlayer(player);
12572
12573   return moved;
12574 }
12575
12576 void ScrollPlayer(struct PlayerInfo *player, int mode)
12577 {
12578   int jx = player->jx, jy = player->jy;
12579   int last_jx = player->last_jx, last_jy = player->last_jy;
12580   int move_stepsize = TILEX / player->move_delay_value;
12581
12582   if (!player->active)
12583     return;
12584
12585   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12586     return;
12587
12588   if (mode == SCROLL_INIT)
12589   {
12590     player->actual_frame_counter = FrameCounter;
12591     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12592
12593     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12594         Feld[last_jx][last_jy] == EL_EMPTY)
12595     {
12596       int last_field_block_delay = 0;   /* start with no blocking at all */
12597       int block_delay_adjustment = player->block_delay_adjustment;
12598
12599       /* if player blocks last field, add delay for exactly one move */
12600       if (player->block_last_field)
12601       {
12602         last_field_block_delay += player->move_delay_value;
12603
12604         /* when blocking enabled, prevent moving up despite gravity */
12605         if (player->gravity && player->MovDir == MV_UP)
12606           block_delay_adjustment = -1;
12607       }
12608
12609       /* add block delay adjustment (also possible when not blocking) */
12610       last_field_block_delay += block_delay_adjustment;
12611
12612       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12613       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12614     }
12615
12616     if (player->MovPos != 0)    /* player has not yet reached destination */
12617       return;
12618   }
12619   else if (!FrameReached(&player->actual_frame_counter, 1))
12620     return;
12621
12622   if (player->MovPos != 0)
12623   {
12624     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12625     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12626
12627     /* before DrawPlayer() to draw correct player graphic for this case */
12628     if (player->MovPos == 0)
12629       CheckGravityMovement(player);
12630   }
12631
12632   if (player->MovPos == 0)      /* player reached destination field */
12633   {
12634     if (player->move_delay_reset_counter > 0)
12635     {
12636       player->move_delay_reset_counter--;
12637
12638       if (player->move_delay_reset_counter == 0)
12639       {
12640         /* continue with normal speed after quickly moving through gate */
12641         HALVE_PLAYER_SPEED(player);
12642
12643         /* be able to make the next move without delay */
12644         player->move_delay = 0;
12645       }
12646     }
12647
12648     player->last_jx = jx;
12649     player->last_jy = jy;
12650
12651     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12652         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12653         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12654         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12655         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12656         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12657         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12658         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12659     {
12660       ExitPlayer(player);
12661
12662       if ((local_player->friends_still_needed == 0 ||
12663            IS_SP_ELEMENT(Feld[jx][jy])) &&
12664           AllPlayersGone)
12665         PlayerWins(local_player);
12666     }
12667
12668     /* this breaks one level: "machine", level 000 */
12669     {
12670       int move_direction = player->MovDir;
12671       int enter_side = MV_DIR_OPPOSITE(move_direction);
12672       int leave_side = move_direction;
12673       int old_jx = last_jx;
12674       int old_jy = last_jy;
12675       int old_element = Feld[old_jx][old_jy];
12676       int new_element = Feld[jx][jy];
12677
12678       if (IS_CUSTOM_ELEMENT(old_element))
12679         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12680                                    CE_LEFT_BY_PLAYER,
12681                                    player->index_bit, leave_side);
12682
12683       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12684                                           CE_PLAYER_LEAVES_X,
12685                                           player->index_bit, leave_side);
12686
12687       if (IS_CUSTOM_ELEMENT(new_element))
12688         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12689                                    player->index_bit, enter_side);
12690
12691       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12692                                           CE_PLAYER_ENTERS_X,
12693                                           player->index_bit, enter_side);
12694
12695       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12696                                         CE_MOVE_OF_X, move_direction);
12697     }
12698
12699     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12700     {
12701       TestIfPlayerTouchesBadThing(jx, jy);
12702       TestIfPlayerTouchesCustomElement(jx, jy);
12703
12704       /* needed because pushed element has not yet reached its destination,
12705          so it would trigger a change event at its previous field location */
12706       if (!player->is_pushing)
12707         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12708
12709       if (!player->active)
12710         RemovePlayer(player);
12711     }
12712
12713     if (!local_player->LevelSolved && level.use_step_counter)
12714     {
12715       int i;
12716
12717       TimePlayed++;
12718
12719       if (TimeLeft > 0)
12720       {
12721         TimeLeft--;
12722
12723         if (TimeLeft <= 10 && setup.time_limit)
12724           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12725
12726         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12727
12728         DisplayGameControlValues();
12729
12730         if (!TimeLeft && setup.time_limit)
12731           for (i = 0; i < MAX_PLAYERS; i++)
12732             KillPlayer(&stored_player[i]);
12733       }
12734       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12735       {
12736         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12737
12738         DisplayGameControlValues();
12739       }
12740     }
12741
12742     if (tape.single_step && tape.recording && !tape.pausing &&
12743         !player->programmed_action)
12744       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12745
12746     if (!player->programmed_action)
12747       CheckSaveEngineSnapshot(player);
12748   }
12749 }
12750
12751 void ScrollScreen(struct PlayerInfo *player, int mode)
12752 {
12753   static unsigned int screen_frame_counter = 0;
12754
12755   if (mode == SCROLL_INIT)
12756   {
12757     /* set scrolling step size according to actual player's moving speed */
12758     ScrollStepSize = TILEX / player->move_delay_value;
12759
12760     screen_frame_counter = FrameCounter;
12761     ScreenMovDir = player->MovDir;
12762     ScreenMovPos = player->MovPos;
12763     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12764     return;
12765   }
12766   else if (!FrameReached(&screen_frame_counter, 1))
12767     return;
12768
12769   if (ScreenMovPos)
12770   {
12771     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12772     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12773     redraw_mask |= REDRAW_FIELD;
12774   }
12775   else
12776     ScreenMovDir = MV_NONE;
12777 }
12778
12779 void TestIfPlayerTouchesCustomElement(int x, int y)
12780 {
12781   static int xy[4][2] =
12782   {
12783     { 0, -1 },
12784     { -1, 0 },
12785     { +1, 0 },
12786     { 0, +1 }
12787   };
12788   static int trigger_sides[4][2] =
12789   {
12790     /* center side       border side */
12791     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12792     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12793     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12794     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12795   };
12796   static int touch_dir[4] =
12797   {
12798     MV_LEFT | MV_RIGHT,
12799     MV_UP   | MV_DOWN,
12800     MV_UP   | MV_DOWN,
12801     MV_LEFT | MV_RIGHT
12802   };
12803   int center_element = Feld[x][y];      /* should always be non-moving! */
12804   int i;
12805
12806   for (i = 0; i < NUM_DIRECTIONS; i++)
12807   {
12808     int xx = x + xy[i][0];
12809     int yy = y + xy[i][1];
12810     int center_side = trigger_sides[i][0];
12811     int border_side = trigger_sides[i][1];
12812     int border_element;
12813
12814     if (!IN_LEV_FIELD(xx, yy))
12815       continue;
12816
12817     if (IS_PLAYER(x, y))                /* player found at center element */
12818     {
12819       struct PlayerInfo *player = PLAYERINFO(x, y);
12820
12821       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12822         border_element = Feld[xx][yy];          /* may be moving! */
12823       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12824         border_element = Feld[xx][yy];
12825       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12826         border_element = MovingOrBlocked2Element(xx, yy);
12827       else
12828         continue;               /* center and border element do not touch */
12829
12830       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12831                                  player->index_bit, border_side);
12832       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12833                                           CE_PLAYER_TOUCHES_X,
12834                                           player->index_bit, border_side);
12835
12836       {
12837         /* use player element that is initially defined in the level playfield,
12838            not the player element that corresponds to the runtime player number
12839            (example: a level that contains EL_PLAYER_3 as the only player would
12840            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12841         int player_element = PLAYERINFO(x, y)->initial_element;
12842
12843         CheckElementChangeBySide(xx, yy, border_element, player_element,
12844                                  CE_TOUCHING_X, border_side);
12845       }
12846     }
12847     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12848     {
12849       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12850
12851       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12852       {
12853         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12854           continue;             /* center and border element do not touch */
12855       }
12856
12857       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12858                                  player->index_bit, center_side);
12859       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12860                                           CE_PLAYER_TOUCHES_X,
12861                                           player->index_bit, center_side);
12862
12863       {
12864         /* use player element that is initially defined in the level playfield,
12865            not the player element that corresponds to the runtime player number
12866            (example: a level that contains EL_PLAYER_3 as the only player would
12867            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12868         int player_element = PLAYERINFO(xx, yy)->initial_element;
12869
12870         CheckElementChangeBySide(x, y, center_element, player_element,
12871                                  CE_TOUCHING_X, center_side);
12872       }
12873
12874       break;
12875     }
12876   }
12877 }
12878
12879 void TestIfElementTouchesCustomElement(int x, int y)
12880 {
12881   static int xy[4][2] =
12882   {
12883     { 0, -1 },
12884     { -1, 0 },
12885     { +1, 0 },
12886     { 0, +1 }
12887   };
12888   static int trigger_sides[4][2] =
12889   {
12890     /* center side      border side */
12891     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12892     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12893     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12894     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12895   };
12896   static int touch_dir[4] =
12897   {
12898     MV_LEFT | MV_RIGHT,
12899     MV_UP   | MV_DOWN,
12900     MV_UP   | MV_DOWN,
12901     MV_LEFT | MV_RIGHT
12902   };
12903   boolean change_center_element = FALSE;
12904   int center_element = Feld[x][y];      /* should always be non-moving! */
12905   int border_element_old[NUM_DIRECTIONS];
12906   int i;
12907
12908   for (i = 0; i < NUM_DIRECTIONS; i++)
12909   {
12910     int xx = x + xy[i][0];
12911     int yy = y + xy[i][1];
12912     int border_element;
12913
12914     border_element_old[i] = -1;
12915
12916     if (!IN_LEV_FIELD(xx, yy))
12917       continue;
12918
12919     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12920       border_element = Feld[xx][yy];    /* may be moving! */
12921     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12922       border_element = Feld[xx][yy];
12923     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12924       border_element = MovingOrBlocked2Element(xx, yy);
12925     else
12926       continue;                 /* center and border element do not touch */
12927
12928     border_element_old[i] = border_element;
12929   }
12930
12931   for (i = 0; i < NUM_DIRECTIONS; i++)
12932   {
12933     int xx = x + xy[i][0];
12934     int yy = y + xy[i][1];
12935     int center_side = trigger_sides[i][0];
12936     int border_element = border_element_old[i];
12937
12938     if (border_element == -1)
12939       continue;
12940
12941     /* check for change of border element */
12942     CheckElementChangeBySide(xx, yy, border_element, center_element,
12943                              CE_TOUCHING_X, center_side);
12944
12945     /* (center element cannot be player, so we dont have to check this here) */
12946   }
12947
12948   for (i = 0; i < NUM_DIRECTIONS; i++)
12949   {
12950     int xx = x + xy[i][0];
12951     int yy = y + xy[i][1];
12952     int border_side = trigger_sides[i][1];
12953     int border_element = border_element_old[i];
12954
12955     if (border_element == -1)
12956       continue;
12957
12958     /* check for change of center element (but change it only once) */
12959     if (!change_center_element)
12960       change_center_element =
12961         CheckElementChangeBySide(x, y, center_element, border_element,
12962                                  CE_TOUCHING_X, border_side);
12963
12964     if (IS_PLAYER(xx, yy))
12965     {
12966       /* use player element that is initially defined in the level playfield,
12967          not the player element that corresponds to the runtime player number
12968          (example: a level that contains EL_PLAYER_3 as the only player would
12969          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12970       int player_element = PLAYERINFO(xx, yy)->initial_element;
12971
12972       CheckElementChangeBySide(x, y, center_element, player_element,
12973                                CE_TOUCHING_X, border_side);
12974     }
12975   }
12976 }
12977
12978 void TestIfElementHitsCustomElement(int x, int y, int direction)
12979 {
12980   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12981   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12982   int hitx = x + dx, hity = y + dy;
12983   int hitting_element = Feld[x][y];
12984   int touched_element;
12985
12986   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12987     return;
12988
12989   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12990                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12991
12992   if (IN_LEV_FIELD(hitx, hity))
12993   {
12994     int opposite_direction = MV_DIR_OPPOSITE(direction);
12995     int hitting_side = direction;
12996     int touched_side = opposite_direction;
12997     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12998                           MovDir[hitx][hity] != direction ||
12999                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13000
13001     object_hit = TRUE;
13002
13003     if (object_hit)
13004     {
13005       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13006                                CE_HITTING_X, touched_side);
13007
13008       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13009                                CE_HIT_BY_X, hitting_side);
13010
13011       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13012                                CE_HIT_BY_SOMETHING, opposite_direction);
13013
13014       if (IS_PLAYER(hitx, hity))
13015       {
13016         /* use player element that is initially defined in the level playfield,
13017            not the player element that corresponds to the runtime player number
13018            (example: a level that contains EL_PLAYER_3 as the only player would
13019            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13020         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13021
13022         CheckElementChangeBySide(x, y, hitting_element, player_element,
13023                                  CE_HITTING_X, touched_side);
13024       }
13025     }
13026   }
13027
13028   /* "hitting something" is also true when hitting the playfield border */
13029   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13030                            CE_HITTING_SOMETHING, direction);
13031 }
13032
13033 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13034 {
13035   int i, kill_x = -1, kill_y = -1;
13036
13037   int bad_element = -1;
13038   static int test_xy[4][2] =
13039   {
13040     { 0, -1 },
13041     { -1, 0 },
13042     { +1, 0 },
13043     { 0, +1 }
13044   };
13045   static int test_dir[4] =
13046   {
13047     MV_UP,
13048     MV_LEFT,
13049     MV_RIGHT,
13050     MV_DOWN
13051   };
13052
13053   for (i = 0; i < NUM_DIRECTIONS; i++)
13054   {
13055     int test_x, test_y, test_move_dir, test_element;
13056
13057     test_x = good_x + test_xy[i][0];
13058     test_y = good_y + test_xy[i][1];
13059
13060     if (!IN_LEV_FIELD(test_x, test_y))
13061       continue;
13062
13063     test_move_dir =
13064       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13065
13066     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13067
13068     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13069        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13070     */
13071     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13072         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13073     {
13074       kill_x = test_x;
13075       kill_y = test_y;
13076       bad_element = test_element;
13077
13078       break;
13079     }
13080   }
13081
13082   if (kill_x != -1 || kill_y != -1)
13083   {
13084     if (IS_PLAYER(good_x, good_y))
13085     {
13086       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13087
13088       if (player->shield_deadly_time_left > 0 &&
13089           !IS_INDESTRUCTIBLE(bad_element))
13090         Bang(kill_x, kill_y);
13091       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13092         KillPlayer(player);
13093     }
13094     else
13095       Bang(good_x, good_y);
13096   }
13097 }
13098
13099 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13100 {
13101   int i, kill_x = -1, kill_y = -1;
13102   int bad_element = Feld[bad_x][bad_y];
13103   static int test_xy[4][2] =
13104   {
13105     { 0, -1 },
13106     { -1, 0 },
13107     { +1, 0 },
13108     { 0, +1 }
13109   };
13110   static int touch_dir[4] =
13111   {
13112     MV_LEFT | MV_RIGHT,
13113     MV_UP   | MV_DOWN,
13114     MV_UP   | MV_DOWN,
13115     MV_LEFT | MV_RIGHT
13116   };
13117   static int test_dir[4] =
13118   {
13119     MV_UP,
13120     MV_LEFT,
13121     MV_RIGHT,
13122     MV_DOWN
13123   };
13124
13125   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13126     return;
13127
13128   for (i = 0; i < NUM_DIRECTIONS; i++)
13129   {
13130     int test_x, test_y, test_move_dir, test_element;
13131
13132     test_x = bad_x + test_xy[i][0];
13133     test_y = bad_y + test_xy[i][1];
13134
13135     if (!IN_LEV_FIELD(test_x, test_y))
13136       continue;
13137
13138     test_move_dir =
13139       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13140
13141     test_element = Feld[test_x][test_y];
13142
13143     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13144        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13145     */
13146     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13147         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13148     {
13149       /* good thing is player or penguin that does not move away */
13150       if (IS_PLAYER(test_x, test_y))
13151       {
13152         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13153
13154         if (bad_element == EL_ROBOT && player->is_moving)
13155           continue;     /* robot does not kill player if he is moving */
13156
13157         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13158         {
13159           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13160             continue;           /* center and border element do not touch */
13161         }
13162
13163         kill_x = test_x;
13164         kill_y = test_y;
13165
13166         break;
13167       }
13168       else if (test_element == EL_PENGUIN)
13169       {
13170         kill_x = test_x;
13171         kill_y = test_y;
13172
13173         break;
13174       }
13175     }
13176   }
13177
13178   if (kill_x != -1 || kill_y != -1)
13179   {
13180     if (IS_PLAYER(kill_x, kill_y))
13181     {
13182       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13183
13184       if (player->shield_deadly_time_left > 0 &&
13185           !IS_INDESTRUCTIBLE(bad_element))
13186         Bang(bad_x, bad_y);
13187       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13188         KillPlayer(player);
13189     }
13190     else
13191       Bang(kill_x, kill_y);
13192   }
13193 }
13194
13195 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13196 {
13197   int bad_element = Feld[bad_x][bad_y];
13198   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13199   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13200   int test_x = bad_x + dx, test_y = bad_y + dy;
13201   int test_move_dir, test_element;
13202   int kill_x = -1, kill_y = -1;
13203
13204   if (!IN_LEV_FIELD(test_x, test_y))
13205     return;
13206
13207   test_move_dir =
13208     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13209
13210   test_element = Feld[test_x][test_y];
13211
13212   if (test_move_dir != bad_move_dir)
13213   {
13214     /* good thing can be player or penguin that does not move away */
13215     if (IS_PLAYER(test_x, test_y))
13216     {
13217       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13218
13219       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13220          player as being hit when he is moving towards the bad thing, because
13221          the "get hit by" condition would be lost after the player stops) */
13222       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13223         return;         /* player moves away from bad thing */
13224
13225       kill_x = test_x;
13226       kill_y = test_y;
13227     }
13228     else if (test_element == EL_PENGUIN)
13229     {
13230       kill_x = test_x;
13231       kill_y = test_y;
13232     }
13233   }
13234
13235   if (kill_x != -1 || kill_y != -1)
13236   {
13237     if (IS_PLAYER(kill_x, kill_y))
13238     {
13239       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13240
13241       if (player->shield_deadly_time_left > 0 &&
13242           !IS_INDESTRUCTIBLE(bad_element))
13243         Bang(bad_x, bad_y);
13244       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13245         KillPlayer(player);
13246     }
13247     else
13248       Bang(kill_x, kill_y);
13249   }
13250 }
13251
13252 void TestIfPlayerTouchesBadThing(int x, int y)
13253 {
13254   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13255 }
13256
13257 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13258 {
13259   TestIfGoodThingHitsBadThing(x, y, move_dir);
13260 }
13261
13262 void TestIfBadThingTouchesPlayer(int x, int y)
13263 {
13264   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13265 }
13266
13267 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13268 {
13269   TestIfBadThingHitsGoodThing(x, y, move_dir);
13270 }
13271
13272 void TestIfFriendTouchesBadThing(int x, int y)
13273 {
13274   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13275 }
13276
13277 void TestIfBadThingTouchesFriend(int x, int y)
13278 {
13279   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13280 }
13281
13282 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13283 {
13284   int i, kill_x = bad_x, kill_y = bad_y;
13285   static int xy[4][2] =
13286   {
13287     { 0, -1 },
13288     { -1, 0 },
13289     { +1, 0 },
13290     { 0, +1 }
13291   };
13292
13293   for (i = 0; i < NUM_DIRECTIONS; i++)
13294   {
13295     int x, y, element;
13296
13297     x = bad_x + xy[i][0];
13298     y = bad_y + xy[i][1];
13299     if (!IN_LEV_FIELD(x, y))
13300       continue;
13301
13302     element = Feld[x][y];
13303     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13304         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13305     {
13306       kill_x = x;
13307       kill_y = y;
13308       break;
13309     }
13310   }
13311
13312   if (kill_x != bad_x || kill_y != bad_y)
13313     Bang(bad_x, bad_y);
13314 }
13315
13316 void KillPlayer(struct PlayerInfo *player)
13317 {
13318   int jx = player->jx, jy = player->jy;
13319
13320   if (!player->active)
13321     return;
13322
13323 #if 0
13324   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13325          player->killed, player->active, player->reanimated);
13326 #endif
13327
13328   /* the following code was introduced to prevent an infinite loop when calling
13329      -> Bang()
13330      -> CheckTriggeredElementChangeExt()
13331      -> ExecuteCustomElementAction()
13332      -> KillPlayer()
13333      -> (infinitely repeating the above sequence of function calls)
13334      which occurs when killing the player while having a CE with the setting
13335      "kill player X when explosion of <player X>"; the solution using a new
13336      field "player->killed" was chosen for backwards compatibility, although
13337      clever use of the fields "player->active" etc. would probably also work */
13338 #if 1
13339   if (player->killed)
13340     return;
13341 #endif
13342
13343   player->killed = TRUE;
13344
13345   /* remove accessible field at the player's position */
13346   Feld[jx][jy] = EL_EMPTY;
13347
13348   /* deactivate shield (else Bang()/Explode() would not work right) */
13349   player->shield_normal_time_left = 0;
13350   player->shield_deadly_time_left = 0;
13351
13352 #if 0
13353   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13354          player->killed, player->active, player->reanimated);
13355 #endif
13356
13357   Bang(jx, jy);
13358
13359 #if 0
13360   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13361          player->killed, player->active, player->reanimated);
13362 #endif
13363
13364   if (player->reanimated)       /* killed player may have been reanimated */
13365     player->killed = player->reanimated = FALSE;
13366   else
13367     BuryPlayer(player);
13368 }
13369
13370 static void KillPlayerUnlessEnemyProtected(int x, int y)
13371 {
13372   if (!PLAYER_ENEMY_PROTECTED(x, y))
13373     KillPlayer(PLAYERINFO(x, y));
13374 }
13375
13376 static void KillPlayerUnlessExplosionProtected(int x, int y)
13377 {
13378   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13379     KillPlayer(PLAYERINFO(x, y));
13380 }
13381
13382 void BuryPlayer(struct PlayerInfo *player)
13383 {
13384   int jx = player->jx, jy = player->jy;
13385
13386   if (!player->active)
13387     return;
13388
13389   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13390   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13391
13392   player->GameOver = TRUE;
13393   RemovePlayer(player);
13394 }
13395
13396 void RemovePlayer(struct PlayerInfo *player)
13397 {
13398   int jx = player->jx, jy = player->jy;
13399   int i, found = FALSE;
13400
13401   player->present = FALSE;
13402   player->active = FALSE;
13403
13404   if (!ExplodeField[jx][jy])
13405     StorePlayer[jx][jy] = 0;
13406
13407   if (player->is_moving)
13408     TEST_DrawLevelField(player->last_jx, player->last_jy);
13409
13410   for (i = 0; i < MAX_PLAYERS; i++)
13411     if (stored_player[i].active)
13412       found = TRUE;
13413
13414   if (!found)
13415     AllPlayersGone = TRUE;
13416
13417   ExitX = ZX = jx;
13418   ExitY = ZY = jy;
13419 }
13420
13421 void ExitPlayer(struct PlayerInfo *player)
13422 {
13423   DrawPlayer(player);   /* needed here only to cleanup last field */
13424   RemovePlayer(player);
13425
13426   local_player->players_still_needed--;
13427 }
13428
13429 static void setFieldForSnapping(int x, int y, int element, int direction)
13430 {
13431   struct ElementInfo *ei = &element_info[element];
13432   int direction_bit = MV_DIR_TO_BIT(direction);
13433   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13434   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13435                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13436
13437   Feld[x][y] = EL_ELEMENT_SNAPPING;
13438   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13439
13440   ResetGfxAnimation(x, y);
13441
13442   GfxElement[x][y] = element;
13443   GfxAction[x][y] = action;
13444   GfxDir[x][y] = direction;
13445   GfxFrame[x][y] = -1;
13446 }
13447
13448 /*
13449   =============================================================================
13450   checkDiagonalPushing()
13451   -----------------------------------------------------------------------------
13452   check if diagonal input device direction results in pushing of object
13453   (by checking if the alternative direction is walkable, diggable, ...)
13454   =============================================================================
13455 */
13456
13457 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13458                                     int x, int y, int real_dx, int real_dy)
13459 {
13460   int jx, jy, dx, dy, xx, yy;
13461
13462   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13463     return TRUE;
13464
13465   /* diagonal direction: check alternative direction */
13466   jx = player->jx;
13467   jy = player->jy;
13468   dx = x - jx;
13469   dy = y - jy;
13470   xx = jx + (dx == 0 ? real_dx : 0);
13471   yy = jy + (dy == 0 ? real_dy : 0);
13472
13473   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13474 }
13475
13476 /*
13477   =============================================================================
13478   DigField()
13479   -----------------------------------------------------------------------------
13480   x, y:                 field next to player (non-diagonal) to try to dig to
13481   real_dx, real_dy:     direction as read from input device (can be diagonal)
13482   =============================================================================
13483 */
13484
13485 static int DigField(struct PlayerInfo *player,
13486                     int oldx, int oldy, int x, int y,
13487                     int real_dx, int real_dy, int mode)
13488 {
13489   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13490   boolean player_was_pushing = player->is_pushing;
13491   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13492   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13493   int jx = oldx, jy = oldy;
13494   int dx = x - jx, dy = y - jy;
13495   int nextx = x + dx, nexty = y + dy;
13496   int move_direction = (dx == -1 ? MV_LEFT  :
13497                         dx == +1 ? MV_RIGHT :
13498                         dy == -1 ? MV_UP    :
13499                         dy == +1 ? MV_DOWN  : MV_NONE);
13500   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13501   int dig_side = MV_DIR_OPPOSITE(move_direction);
13502   int old_element = Feld[jx][jy];
13503   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13504   int collect_count;
13505
13506   if (is_player)                /* function can also be called by EL_PENGUIN */
13507   {
13508     if (player->MovPos == 0)
13509     {
13510       player->is_digging = FALSE;
13511       player->is_collecting = FALSE;
13512     }
13513
13514     if (player->MovPos == 0)    /* last pushing move finished */
13515       player->is_pushing = FALSE;
13516
13517     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13518     {
13519       player->is_switching = FALSE;
13520       player->push_delay = -1;
13521
13522       return MP_NO_ACTION;
13523     }
13524   }
13525
13526   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13527     old_element = Back[jx][jy];
13528
13529   /* in case of element dropped at player position, check background */
13530   else if (Back[jx][jy] != EL_EMPTY &&
13531            game.engine_version >= VERSION_IDENT(2,2,0,0))
13532     old_element = Back[jx][jy];
13533
13534   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13535     return MP_NO_ACTION;        /* field has no opening in this direction */
13536
13537   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13538     return MP_NO_ACTION;        /* field has no opening in this direction */
13539
13540   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13541   {
13542     SplashAcid(x, y);
13543
13544     Feld[jx][jy] = player->artwork_element;
13545     InitMovingField(jx, jy, MV_DOWN);
13546     Store[jx][jy] = EL_ACID;
13547     ContinueMoving(jx, jy);
13548     BuryPlayer(player);
13549
13550     return MP_DONT_RUN_INTO;
13551   }
13552
13553   if (player_can_move && DONT_RUN_INTO(element))
13554   {
13555     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13556
13557     return MP_DONT_RUN_INTO;
13558   }
13559
13560   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13561     return MP_NO_ACTION;
13562
13563   collect_count = element_info[element].collect_count_initial;
13564
13565   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13566     return MP_NO_ACTION;
13567
13568   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13569     player_can_move = player_can_move_or_snap;
13570
13571   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13572       game.engine_version >= VERSION_IDENT(2,2,0,0))
13573   {
13574     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13575                                player->index_bit, dig_side);
13576     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13577                                         player->index_bit, dig_side);
13578
13579     if (element == EL_DC_LANDMINE)
13580       Bang(x, y);
13581
13582     if (Feld[x][y] != element)          /* field changed by snapping */
13583       return MP_ACTION;
13584
13585     return MP_NO_ACTION;
13586   }
13587
13588   if (player->gravity && is_player && !player->is_auto_moving &&
13589       canFallDown(player) && move_direction != MV_DOWN &&
13590       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13591     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13592
13593   if (player_can_move &&
13594       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13595   {
13596     int sound_element = SND_ELEMENT(element);
13597     int sound_action = ACTION_WALKING;
13598
13599     if (IS_RND_GATE(element))
13600     {
13601       if (!player->key[RND_GATE_NR(element)])
13602         return MP_NO_ACTION;
13603     }
13604     else if (IS_RND_GATE_GRAY(element))
13605     {
13606       if (!player->key[RND_GATE_GRAY_NR(element)])
13607         return MP_NO_ACTION;
13608     }
13609     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13610     {
13611       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13612         return MP_NO_ACTION;
13613     }
13614     else if (element == EL_EXIT_OPEN ||
13615              element == EL_EM_EXIT_OPEN ||
13616              element == EL_EM_EXIT_OPENING ||
13617              element == EL_STEEL_EXIT_OPEN ||
13618              element == EL_EM_STEEL_EXIT_OPEN ||
13619              element == EL_EM_STEEL_EXIT_OPENING ||
13620              element == EL_SP_EXIT_OPEN ||
13621              element == EL_SP_EXIT_OPENING)
13622     {
13623       sound_action = ACTION_PASSING;    /* player is passing exit */
13624     }
13625     else if (element == EL_EMPTY)
13626     {
13627       sound_action = ACTION_MOVING;             /* nothing to walk on */
13628     }
13629
13630     /* play sound from background or player, whatever is available */
13631     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13632       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13633     else
13634       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13635   }
13636   else if (player_can_move &&
13637            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13638   {
13639     if (!ACCESS_FROM(element, opposite_direction))
13640       return MP_NO_ACTION;      /* field not accessible from this direction */
13641
13642     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13643       return MP_NO_ACTION;
13644
13645     if (IS_EM_GATE(element))
13646     {
13647       if (!player->key[EM_GATE_NR(element)])
13648         return MP_NO_ACTION;
13649     }
13650     else if (IS_EM_GATE_GRAY(element))
13651     {
13652       if (!player->key[EM_GATE_GRAY_NR(element)])
13653         return MP_NO_ACTION;
13654     }
13655     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13656     {
13657       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13658         return MP_NO_ACTION;
13659     }
13660     else if (IS_EMC_GATE(element))
13661     {
13662       if (!player->key[EMC_GATE_NR(element)])
13663         return MP_NO_ACTION;
13664     }
13665     else if (IS_EMC_GATE_GRAY(element))
13666     {
13667       if (!player->key[EMC_GATE_GRAY_NR(element)])
13668         return MP_NO_ACTION;
13669     }
13670     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13671     {
13672       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13673         return MP_NO_ACTION;
13674     }
13675     else if (element == EL_DC_GATE_WHITE ||
13676              element == EL_DC_GATE_WHITE_GRAY ||
13677              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13678     {
13679       if (player->num_white_keys == 0)
13680         return MP_NO_ACTION;
13681
13682       player->num_white_keys--;
13683     }
13684     else if (IS_SP_PORT(element))
13685     {
13686       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13687           element == EL_SP_GRAVITY_PORT_RIGHT ||
13688           element == EL_SP_GRAVITY_PORT_UP ||
13689           element == EL_SP_GRAVITY_PORT_DOWN)
13690         player->gravity = !player->gravity;
13691       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13692                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13693                element == EL_SP_GRAVITY_ON_PORT_UP ||
13694                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13695         player->gravity = TRUE;
13696       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13697                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13698                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13699                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13700         player->gravity = FALSE;
13701     }
13702
13703     /* automatically move to the next field with double speed */
13704     player->programmed_action = move_direction;
13705
13706     if (player->move_delay_reset_counter == 0)
13707     {
13708       player->move_delay_reset_counter = 2;     /* two double speed steps */
13709
13710       DOUBLE_PLAYER_SPEED(player);
13711     }
13712
13713     PlayLevelSoundAction(x, y, ACTION_PASSING);
13714   }
13715   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13716   {
13717     RemoveField(x, y);
13718
13719     if (mode != DF_SNAP)
13720     {
13721       GfxElement[x][y] = GFX_ELEMENT(element);
13722       player->is_digging = TRUE;
13723     }
13724
13725     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13726
13727     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13728                                         player->index_bit, dig_side);
13729
13730     if (mode == DF_SNAP)
13731     {
13732       if (level.block_snap_field)
13733         setFieldForSnapping(x, y, element, move_direction);
13734       else
13735         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13736
13737       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13738                                           player->index_bit, dig_side);
13739     }
13740   }
13741   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13742   {
13743     RemoveField(x, y);
13744
13745     if (is_player && mode != DF_SNAP)
13746     {
13747       GfxElement[x][y] = element;
13748       player->is_collecting = TRUE;
13749     }
13750
13751     if (element == EL_SPEED_PILL)
13752     {
13753       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13754     }
13755     else if (element == EL_EXTRA_TIME && level.time > 0)
13756     {
13757       TimeLeft += level.extra_time;
13758
13759       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13760
13761       DisplayGameControlValues();
13762     }
13763     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13764     {
13765       player->shield_normal_time_left += level.shield_normal_time;
13766       if (element == EL_SHIELD_DEADLY)
13767         player->shield_deadly_time_left += level.shield_deadly_time;
13768     }
13769     else if (element == EL_DYNAMITE ||
13770              element == EL_EM_DYNAMITE ||
13771              element == EL_SP_DISK_RED)
13772     {
13773       if (player->inventory_size < MAX_INVENTORY_SIZE)
13774         player->inventory_element[player->inventory_size++] = element;
13775
13776       DrawGameDoorValues();
13777     }
13778     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13779     {
13780       player->dynabomb_count++;
13781       player->dynabombs_left++;
13782     }
13783     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13784     {
13785       player->dynabomb_size++;
13786     }
13787     else if (element == EL_DYNABOMB_INCREASE_POWER)
13788     {
13789       player->dynabomb_xl = TRUE;
13790     }
13791     else if (IS_KEY(element))
13792     {
13793       player->key[KEY_NR(element)] = TRUE;
13794
13795       DrawGameDoorValues();
13796     }
13797     else if (element == EL_DC_KEY_WHITE)
13798     {
13799       player->num_white_keys++;
13800
13801       /* display white keys? */
13802       /* DrawGameDoorValues(); */
13803     }
13804     else if (IS_ENVELOPE(element))
13805     {
13806       player->show_envelope = element;
13807     }
13808     else if (element == EL_EMC_LENSES)
13809     {
13810       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13811
13812       RedrawAllInvisibleElementsForLenses();
13813     }
13814     else if (element == EL_EMC_MAGNIFIER)
13815     {
13816       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13817
13818       RedrawAllInvisibleElementsForMagnifier();
13819     }
13820     else if (IS_DROPPABLE(element) ||
13821              IS_THROWABLE(element))     /* can be collected and dropped */
13822     {
13823       int i;
13824
13825       if (collect_count == 0)
13826         player->inventory_infinite_element = element;
13827       else
13828         for (i = 0; i < collect_count; i++)
13829           if (player->inventory_size < MAX_INVENTORY_SIZE)
13830             player->inventory_element[player->inventory_size++] = element;
13831
13832       DrawGameDoorValues();
13833     }
13834     else if (collect_count > 0)
13835     {
13836       local_player->gems_still_needed -= collect_count;
13837       if (local_player->gems_still_needed < 0)
13838         local_player->gems_still_needed = 0;
13839
13840       game.snapshot.collected_item = TRUE;
13841
13842       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13843
13844       DisplayGameControlValues();
13845     }
13846
13847     RaiseScoreElement(element);
13848     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13849
13850     if (is_player)
13851       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13852                                           player->index_bit, dig_side);
13853
13854     if (mode == DF_SNAP)
13855     {
13856       if (level.block_snap_field)
13857         setFieldForSnapping(x, y, element, move_direction);
13858       else
13859         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13860
13861       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13862                                           player->index_bit, dig_side);
13863     }
13864   }
13865   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13866   {
13867     if (mode == DF_SNAP && element != EL_BD_ROCK)
13868       return MP_NO_ACTION;
13869
13870     if (CAN_FALL(element) && dy)
13871       return MP_NO_ACTION;
13872
13873     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13874         !(element == EL_SPRING && level.use_spring_bug))
13875       return MP_NO_ACTION;
13876
13877     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13878         ((move_direction & MV_VERTICAL &&
13879           ((element_info[element].move_pattern & MV_LEFT &&
13880             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13881            (element_info[element].move_pattern & MV_RIGHT &&
13882             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13883          (move_direction & MV_HORIZONTAL &&
13884           ((element_info[element].move_pattern & MV_UP &&
13885             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13886            (element_info[element].move_pattern & MV_DOWN &&
13887             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13888       return MP_NO_ACTION;
13889
13890     /* do not push elements already moving away faster than player */
13891     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13892         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13893       return MP_NO_ACTION;
13894
13895     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13896     {
13897       if (player->push_delay_value == -1 || !player_was_pushing)
13898         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13899     }
13900     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13901     {
13902       if (player->push_delay_value == -1)
13903         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13904     }
13905     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13906     {
13907       if (!player->is_pushing)
13908         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13909     }
13910
13911     player->is_pushing = TRUE;
13912     player->is_active = TRUE;
13913
13914     if (!(IN_LEV_FIELD(nextx, nexty) &&
13915           (IS_FREE(nextx, nexty) ||
13916            (IS_SB_ELEMENT(element) &&
13917             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13918            (IS_CUSTOM_ELEMENT(element) &&
13919             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13920       return MP_NO_ACTION;
13921
13922     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13923       return MP_NO_ACTION;
13924
13925     if (player->push_delay == -1)       /* new pushing; restart delay */
13926       player->push_delay = 0;
13927
13928     if (player->push_delay < player->push_delay_value &&
13929         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13930         element != EL_SPRING && element != EL_BALLOON)
13931     {
13932       /* make sure that there is no move delay before next try to push */
13933       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13934         player->move_delay = 0;
13935
13936       return MP_NO_ACTION;
13937     }
13938
13939     if (IS_CUSTOM_ELEMENT(element) &&
13940         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13941     {
13942       if (!DigFieldByCE(nextx, nexty, element))
13943         return MP_NO_ACTION;
13944     }
13945
13946     if (IS_SB_ELEMENT(element))
13947     {
13948       if (element == EL_SOKOBAN_FIELD_FULL)
13949       {
13950         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13951         local_player->sokobanfields_still_needed++;
13952       }
13953
13954       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13955       {
13956         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13957         local_player->sokobanfields_still_needed--;
13958       }
13959
13960       Feld[x][y] = EL_SOKOBAN_OBJECT;
13961
13962       if (Back[x][y] == Back[nextx][nexty])
13963         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13964       else if (Back[x][y] != 0)
13965         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13966                                     ACTION_EMPTYING);
13967       else
13968         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13969                                     ACTION_FILLING);
13970
13971       if (local_player->sokobanfields_still_needed == 0 &&
13972           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13973       {
13974         local_player->players_still_needed = 0;
13975
13976         PlayerWins(player);
13977
13978         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13979       }
13980     }
13981     else
13982       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13983
13984     InitMovingField(x, y, move_direction);
13985     GfxAction[x][y] = ACTION_PUSHING;
13986
13987     if (mode == DF_SNAP)
13988       ContinueMoving(x, y);
13989     else
13990       MovPos[x][y] = (dx != 0 ? dx : dy);
13991
13992     Pushed[x][y] = TRUE;
13993     Pushed[nextx][nexty] = TRUE;
13994
13995     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13996       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13997     else
13998       player->push_delay_value = -1;    /* get new value later */
13999
14000     /* check for element change _after_ element has been pushed */
14001     if (game.use_change_when_pushing_bug)
14002     {
14003       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14004                                  player->index_bit, dig_side);
14005       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14006                                           player->index_bit, dig_side);
14007     }
14008   }
14009   else if (IS_SWITCHABLE(element))
14010   {
14011     if (PLAYER_SWITCHING(player, x, y))
14012     {
14013       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14014                                           player->index_bit, dig_side);
14015
14016       return MP_ACTION;
14017     }
14018
14019     player->is_switching = TRUE;
14020     player->switch_x = x;
14021     player->switch_y = y;
14022
14023     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14024
14025     if (element == EL_ROBOT_WHEEL)
14026     {
14027       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14028       ZX = x;
14029       ZY = y;
14030
14031       game.robot_wheel_active = TRUE;
14032
14033       TEST_DrawLevelField(x, y);
14034     }
14035     else if (element == EL_SP_TERMINAL)
14036     {
14037       int xx, yy;
14038
14039       SCAN_PLAYFIELD(xx, yy)
14040       {
14041         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14042         {
14043           Bang(xx, yy);
14044         }
14045         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14046         {
14047           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14048
14049           ResetGfxAnimation(xx, yy);
14050           TEST_DrawLevelField(xx, yy);
14051         }
14052       }
14053     }
14054     else if (IS_BELT_SWITCH(element))
14055     {
14056       ToggleBeltSwitch(x, y);
14057     }
14058     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14059              element == EL_SWITCHGATE_SWITCH_DOWN ||
14060              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14061              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14062     {
14063       ToggleSwitchgateSwitch(x, y);
14064     }
14065     else if (element == EL_LIGHT_SWITCH ||
14066              element == EL_LIGHT_SWITCH_ACTIVE)
14067     {
14068       ToggleLightSwitch(x, y);
14069     }
14070     else if (element == EL_TIMEGATE_SWITCH ||
14071              element == EL_DC_TIMEGATE_SWITCH)
14072     {
14073       ActivateTimegateSwitch(x, y);
14074     }
14075     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14076              element == EL_BALLOON_SWITCH_RIGHT ||
14077              element == EL_BALLOON_SWITCH_UP    ||
14078              element == EL_BALLOON_SWITCH_DOWN  ||
14079              element == EL_BALLOON_SWITCH_NONE  ||
14080              element == EL_BALLOON_SWITCH_ANY)
14081     {
14082       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14083                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14084                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14085                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14086                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14087                              move_direction);
14088     }
14089     else if (element == EL_LAMP)
14090     {
14091       Feld[x][y] = EL_LAMP_ACTIVE;
14092       local_player->lights_still_needed--;
14093
14094       ResetGfxAnimation(x, y);
14095       TEST_DrawLevelField(x, y);
14096     }
14097     else if (element == EL_TIME_ORB_FULL)
14098     {
14099       Feld[x][y] = EL_TIME_ORB_EMPTY;
14100
14101       if (level.time > 0 || level.use_time_orb_bug)
14102       {
14103         TimeLeft += level.time_orb_time;
14104         game.no_time_limit = FALSE;
14105
14106         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14107
14108         DisplayGameControlValues();
14109       }
14110
14111       ResetGfxAnimation(x, y);
14112       TEST_DrawLevelField(x, y);
14113     }
14114     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14115              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14116     {
14117       int xx, yy;
14118
14119       game.ball_state = !game.ball_state;
14120
14121       SCAN_PLAYFIELD(xx, yy)
14122       {
14123         int e = Feld[xx][yy];
14124
14125         if (game.ball_state)
14126         {
14127           if (e == EL_EMC_MAGIC_BALL)
14128             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14129           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14130             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14131         }
14132         else
14133         {
14134           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14135             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14136           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14137             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14138         }
14139       }
14140     }
14141
14142     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14143                                         player->index_bit, dig_side);
14144
14145     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14146                                         player->index_bit, dig_side);
14147
14148     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14149                                         player->index_bit, dig_side);
14150
14151     return MP_ACTION;
14152   }
14153   else
14154   {
14155     if (!PLAYER_SWITCHING(player, x, y))
14156     {
14157       player->is_switching = TRUE;
14158       player->switch_x = x;
14159       player->switch_y = y;
14160
14161       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14162                                  player->index_bit, dig_side);
14163       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14164                                           player->index_bit, dig_side);
14165
14166       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14167                                  player->index_bit, dig_side);
14168       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14169                                           player->index_bit, dig_side);
14170     }
14171
14172     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14173                                player->index_bit, dig_side);
14174     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14175                                         player->index_bit, dig_side);
14176
14177     return MP_NO_ACTION;
14178   }
14179
14180   player->push_delay = -1;
14181
14182   if (is_player)                /* function can also be called by EL_PENGUIN */
14183   {
14184     if (Feld[x][y] != element)          /* really digged/collected something */
14185     {
14186       player->is_collecting = !player->is_digging;
14187       player->is_active = TRUE;
14188     }
14189   }
14190
14191   return MP_MOVING;
14192 }
14193
14194 static boolean DigFieldByCE(int x, int y, int digging_element)
14195 {
14196   int element = Feld[x][y];
14197
14198   if (!IS_FREE(x, y))
14199   {
14200     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14201                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14202                   ACTION_BREAKING);
14203
14204     /* no element can dig solid indestructible elements */
14205     if (IS_INDESTRUCTIBLE(element) &&
14206         !IS_DIGGABLE(element) &&
14207         !IS_COLLECTIBLE(element))
14208       return FALSE;
14209
14210     if (AmoebaNr[x][y] &&
14211         (element == EL_AMOEBA_FULL ||
14212          element == EL_BD_AMOEBA ||
14213          element == EL_AMOEBA_GROWING))
14214     {
14215       AmoebaCnt[AmoebaNr[x][y]]--;
14216       AmoebaCnt2[AmoebaNr[x][y]]--;
14217     }
14218
14219     if (IS_MOVING(x, y))
14220       RemoveMovingField(x, y);
14221     else
14222     {
14223       RemoveField(x, y);
14224       TEST_DrawLevelField(x, y);
14225     }
14226
14227     /* if digged element was about to explode, prevent the explosion */
14228     ExplodeField[x][y] = EX_TYPE_NONE;
14229
14230     PlayLevelSoundAction(x, y, action);
14231   }
14232
14233   Store[x][y] = EL_EMPTY;
14234
14235   /* this makes it possible to leave the removed element again */
14236   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14237     Store[x][y] = element;
14238
14239   return TRUE;
14240 }
14241
14242 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14243 {
14244   int jx = player->jx, jy = player->jy;
14245   int x = jx + dx, y = jy + dy;
14246   int snap_direction = (dx == -1 ? MV_LEFT  :
14247                         dx == +1 ? MV_RIGHT :
14248                         dy == -1 ? MV_UP    :
14249                         dy == +1 ? MV_DOWN  : MV_NONE);
14250   boolean can_continue_snapping = (level.continuous_snapping &&
14251                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14252
14253   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14254     return FALSE;
14255
14256   if (!player->active || !IN_LEV_FIELD(x, y))
14257     return FALSE;
14258
14259   if (dx && dy)
14260     return FALSE;
14261
14262   if (!dx && !dy)
14263   {
14264     if (player->MovPos == 0)
14265       player->is_pushing = FALSE;
14266
14267     player->is_snapping = FALSE;
14268
14269     if (player->MovPos == 0)
14270     {
14271       player->is_moving = FALSE;
14272       player->is_digging = FALSE;
14273       player->is_collecting = FALSE;
14274     }
14275
14276     return FALSE;
14277   }
14278
14279   /* prevent snapping with already pressed snap key when not allowed */
14280   if (player->is_snapping && !can_continue_snapping)
14281     return FALSE;
14282
14283   player->MovDir = snap_direction;
14284
14285   if (player->MovPos == 0)
14286   {
14287     player->is_moving = FALSE;
14288     player->is_digging = FALSE;
14289     player->is_collecting = FALSE;
14290   }
14291
14292   player->is_dropping = FALSE;
14293   player->is_dropping_pressed = FALSE;
14294   player->drop_pressed_delay = 0;
14295
14296   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14297     return FALSE;
14298
14299   player->is_snapping = TRUE;
14300   player->is_active = TRUE;
14301
14302   if (player->MovPos == 0)
14303   {
14304     player->is_moving = FALSE;
14305     player->is_digging = FALSE;
14306     player->is_collecting = FALSE;
14307   }
14308
14309   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14310     TEST_DrawLevelField(player->last_jx, player->last_jy);
14311
14312   TEST_DrawLevelField(x, y);
14313
14314   return TRUE;
14315 }
14316
14317 static boolean DropElement(struct PlayerInfo *player)
14318 {
14319   int old_element, new_element;
14320   int dropx = player->jx, dropy = player->jy;
14321   int drop_direction = player->MovDir;
14322   int drop_side = drop_direction;
14323   int drop_element = get_next_dropped_element(player);
14324
14325   /* do not drop an element on top of another element; when holding drop key
14326      pressed without moving, dropped element must move away before the next
14327      element can be dropped (this is especially important if the next element
14328      is dynamite, which can be placed on background for historical reasons) */
14329   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14330     return MP_ACTION;
14331
14332   if (IS_THROWABLE(drop_element))
14333   {
14334     dropx += GET_DX_FROM_DIR(drop_direction);
14335     dropy += GET_DY_FROM_DIR(drop_direction);
14336
14337     if (!IN_LEV_FIELD(dropx, dropy))
14338       return FALSE;
14339   }
14340
14341   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14342   new_element = drop_element;           /* default: no change when dropping */
14343
14344   /* check if player is active, not moving and ready to drop */
14345   if (!player->active || player->MovPos || player->drop_delay > 0)
14346     return FALSE;
14347
14348   /* check if player has anything that can be dropped */
14349   if (new_element == EL_UNDEFINED)
14350     return FALSE;
14351
14352   /* only set if player has anything that can be dropped */
14353   player->is_dropping_pressed = TRUE;
14354
14355   /* check if drop key was pressed long enough for EM style dynamite */
14356   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14357     return FALSE;
14358
14359   /* check if anything can be dropped at the current position */
14360   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14361     return FALSE;
14362
14363   /* collected custom elements can only be dropped on empty fields */
14364   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14365     return FALSE;
14366
14367   if (old_element != EL_EMPTY)
14368     Back[dropx][dropy] = old_element;   /* store old element on this field */
14369
14370   ResetGfxAnimation(dropx, dropy);
14371   ResetRandomAnimationValue(dropx, dropy);
14372
14373   if (player->inventory_size > 0 ||
14374       player->inventory_infinite_element != EL_UNDEFINED)
14375   {
14376     if (player->inventory_size > 0)
14377     {
14378       player->inventory_size--;
14379
14380       DrawGameDoorValues();
14381
14382       if (new_element == EL_DYNAMITE)
14383         new_element = EL_DYNAMITE_ACTIVE;
14384       else if (new_element == EL_EM_DYNAMITE)
14385         new_element = EL_EM_DYNAMITE_ACTIVE;
14386       else if (new_element == EL_SP_DISK_RED)
14387         new_element = EL_SP_DISK_RED_ACTIVE;
14388     }
14389
14390     Feld[dropx][dropy] = new_element;
14391
14392     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14393       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14394                           el2img(Feld[dropx][dropy]), 0);
14395
14396     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14397
14398     /* needed if previous element just changed to "empty" in the last frame */
14399     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14400
14401     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14402                                player->index_bit, drop_side);
14403     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14404                                         CE_PLAYER_DROPS_X,
14405                                         player->index_bit, drop_side);
14406
14407     TestIfElementTouchesCustomElement(dropx, dropy);
14408   }
14409   else          /* player is dropping a dyna bomb */
14410   {
14411     player->dynabombs_left--;
14412
14413     Feld[dropx][dropy] = new_element;
14414
14415     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14416       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14417                           el2img(Feld[dropx][dropy]), 0);
14418
14419     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14420   }
14421
14422   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14423     InitField_WithBug1(dropx, dropy, FALSE);
14424
14425   new_element = Feld[dropx][dropy];     /* element might have changed */
14426
14427   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14428       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14429   {
14430     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14431       MovDir[dropx][dropy] = drop_direction;
14432
14433     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14434
14435     /* do not cause impact style collision by dropping elements that can fall */
14436     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14437   }
14438
14439   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14440   player->is_dropping = TRUE;
14441
14442   player->drop_pressed_delay = 0;
14443   player->is_dropping_pressed = FALSE;
14444
14445   player->drop_x = dropx;
14446   player->drop_y = dropy;
14447
14448   return TRUE;
14449 }
14450
14451 /* ------------------------------------------------------------------------- */
14452 /* game sound playing functions                                              */
14453 /* ------------------------------------------------------------------------- */
14454
14455 static int *loop_sound_frame = NULL;
14456 static int *loop_sound_volume = NULL;
14457
14458 void InitPlayLevelSound()
14459 {
14460   int num_sounds = getSoundListSize();
14461
14462   checked_free(loop_sound_frame);
14463   checked_free(loop_sound_volume);
14464
14465   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14466   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14467 }
14468
14469 static void PlayLevelSound(int x, int y, int nr)
14470 {
14471   int sx = SCREENX(x), sy = SCREENY(y);
14472   int volume, stereo_position;
14473   int max_distance = 8;
14474   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14475
14476   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14477       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14478     return;
14479
14480   if (!IN_LEV_FIELD(x, y) ||
14481       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14482       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14483     return;
14484
14485   volume = SOUND_MAX_VOLUME;
14486
14487   if (!IN_SCR_FIELD(sx, sy))
14488   {
14489     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14490     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14491
14492     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14493   }
14494
14495   stereo_position = (SOUND_MAX_LEFT +
14496                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14497                      (SCR_FIELDX + 2 * max_distance));
14498
14499   if (IS_LOOP_SOUND(nr))
14500   {
14501     /* This assures that quieter loop sounds do not overwrite louder ones,
14502        while restarting sound volume comparison with each new game frame. */
14503
14504     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14505       return;
14506
14507     loop_sound_volume[nr] = volume;
14508     loop_sound_frame[nr] = FrameCounter;
14509   }
14510
14511   PlaySoundExt(nr, volume, stereo_position, type);
14512 }
14513
14514 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14515 {
14516   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14517                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14518                  y < LEVELY(BY1) ? LEVELY(BY1) :
14519                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14520                  sound_action);
14521 }
14522
14523 static void PlayLevelSoundAction(int x, int y, int action)
14524 {
14525   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14526 }
14527
14528 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14529 {
14530   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14531
14532   if (sound_effect != SND_UNDEFINED)
14533     PlayLevelSound(x, y, sound_effect);
14534 }
14535
14536 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14537                                               int action)
14538 {
14539   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14540
14541   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14542     PlayLevelSound(x, y, sound_effect);
14543 }
14544
14545 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14546 {
14547   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14548
14549   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14550     PlayLevelSound(x, y, sound_effect);
14551 }
14552
14553 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14554 {
14555   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14556
14557   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14558     StopSound(sound_effect);
14559 }
14560
14561 static int getLevelMusicNr()
14562 {
14563   if (levelset.music[level_nr] != MUS_UNDEFINED)
14564     return levelset.music[level_nr];            /* from config file */
14565   else
14566     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14567 }
14568
14569 static void FadeLevelSounds()
14570 {
14571   FadeSounds();
14572 }
14573
14574 static void FadeLevelMusic()
14575 {
14576   int music_nr = getLevelMusicNr();
14577   char *curr_music = getCurrentlyPlayingMusicFilename();
14578   char *next_music = getMusicInfoEntryFilename(music_nr);
14579
14580   if (!strEqual(curr_music, next_music))
14581     FadeMusic();
14582 }
14583
14584 void FadeLevelSoundsAndMusic()
14585 {
14586   FadeLevelSounds();
14587   FadeLevelMusic();
14588 }
14589
14590 static void PlayLevelMusic()
14591 {
14592   int music_nr = getLevelMusicNr();
14593   char *curr_music = getCurrentlyPlayingMusicFilename();
14594   char *next_music = getMusicInfoEntryFilename(music_nr);
14595
14596   if (!strEqual(curr_music, next_music))
14597     PlayMusic(music_nr);
14598 }
14599
14600 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14601 {
14602   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14603   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14604   int x = xx - 1 - offset;
14605   int y = yy - 1 - offset;
14606
14607   switch (sample)
14608   {
14609     case SAMPLE_blank:
14610       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14611       break;
14612
14613     case SAMPLE_roll:
14614       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14615       break;
14616
14617     case SAMPLE_stone:
14618       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14619       break;
14620
14621     case SAMPLE_nut:
14622       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14623       break;
14624
14625     case SAMPLE_crack:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14627       break;
14628
14629     case SAMPLE_bug:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14631       break;
14632
14633     case SAMPLE_tank:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14635       break;
14636
14637     case SAMPLE_android_clone:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14639       break;
14640
14641     case SAMPLE_android_move:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14643       break;
14644
14645     case SAMPLE_spring:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14647       break;
14648
14649     case SAMPLE_slurp:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14651       break;
14652
14653     case SAMPLE_eater:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14655       break;
14656
14657     case SAMPLE_eater_eat:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14659       break;
14660
14661     case SAMPLE_alien:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14663       break;
14664
14665     case SAMPLE_collect:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14667       break;
14668
14669     case SAMPLE_diamond:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14671       break;
14672
14673     case SAMPLE_squash:
14674       /* !!! CHECK THIS !!! */
14675 #if 1
14676       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14677 #else
14678       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14679 #endif
14680       break;
14681
14682     case SAMPLE_wonderfall:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14684       break;
14685
14686     case SAMPLE_drip:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14688       break;
14689
14690     case SAMPLE_push:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14692       break;
14693
14694     case SAMPLE_dirt:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14696       break;
14697
14698     case SAMPLE_acid:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14700       break;
14701
14702     case SAMPLE_ball:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14704       break;
14705
14706     case SAMPLE_grow:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14708       break;
14709
14710     case SAMPLE_wonder:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14712       break;
14713
14714     case SAMPLE_door:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14716       break;
14717
14718     case SAMPLE_exit_open:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14720       break;
14721
14722     case SAMPLE_exit_leave:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14724       break;
14725
14726     case SAMPLE_dynamite:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14728       break;
14729
14730     case SAMPLE_tick:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14732       break;
14733
14734     case SAMPLE_press:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14736       break;
14737
14738     case SAMPLE_wheel:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14740       break;
14741
14742     case SAMPLE_boom:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14744       break;
14745
14746     case SAMPLE_die:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14748       break;
14749
14750     case SAMPLE_time:
14751       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14752       break;
14753
14754     default:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14756       break;
14757   }
14758 }
14759
14760 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14761 {
14762   int element = map_element_SP_to_RND(element_sp);
14763   int action = map_action_SP_to_RND(action_sp);
14764   int offset = (setup.sp_show_border_elements ? 0 : 1);
14765   int x = xx - offset;
14766   int y = yy - offset;
14767
14768   PlayLevelSoundElementAction(x, y, element, action);
14769 }
14770
14771 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14772 {
14773   int element = map_element_MM_to_RND(element_mm);
14774   int action = map_action_MM_to_RND(action_mm);
14775   int offset = 0;
14776   int x = xx - offset;
14777   int y = yy - offset;
14778
14779   if (!IS_MM_ELEMENT(element))
14780     element = EL_MM_DEFAULT;
14781
14782   PlayLevelSoundElementAction(x, y, element, action);
14783 }
14784
14785 void PlaySound_MM(int sound_mm)
14786 {
14787   int sound = map_sound_MM_to_RND(sound_mm);
14788
14789   if (sound == SND_UNDEFINED)
14790     return;
14791
14792   PlaySound(sound);
14793 }
14794
14795 void PlaySoundLoop_MM(int sound_mm)
14796 {
14797   int sound = map_sound_MM_to_RND(sound_mm);
14798
14799   if (sound == SND_UNDEFINED)
14800     return;
14801
14802   PlaySoundLoop(sound);
14803 }
14804
14805 void StopSound_MM(int sound_mm)
14806 {
14807   int sound = map_sound_MM_to_RND(sound_mm);
14808
14809   if (sound == SND_UNDEFINED)
14810     return;
14811
14812   StopSound(sound);
14813 }
14814
14815 void RaiseScore(int value)
14816 {
14817   local_player->score += value;
14818
14819   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14820
14821   DisplayGameControlValues();
14822 }
14823
14824 void RaiseScoreElement(int element)
14825 {
14826   switch (element)
14827   {
14828     case EL_EMERALD:
14829     case EL_BD_DIAMOND:
14830     case EL_EMERALD_YELLOW:
14831     case EL_EMERALD_RED:
14832     case EL_EMERALD_PURPLE:
14833     case EL_SP_INFOTRON:
14834       RaiseScore(level.score[SC_EMERALD]);
14835       break;
14836     case EL_DIAMOND:
14837       RaiseScore(level.score[SC_DIAMOND]);
14838       break;
14839     case EL_CRYSTAL:
14840       RaiseScore(level.score[SC_CRYSTAL]);
14841       break;
14842     case EL_PEARL:
14843       RaiseScore(level.score[SC_PEARL]);
14844       break;
14845     case EL_BUG:
14846     case EL_BD_BUTTERFLY:
14847     case EL_SP_ELECTRON:
14848       RaiseScore(level.score[SC_BUG]);
14849       break;
14850     case EL_SPACESHIP:
14851     case EL_BD_FIREFLY:
14852     case EL_SP_SNIKSNAK:
14853       RaiseScore(level.score[SC_SPACESHIP]);
14854       break;
14855     case EL_YAMYAM:
14856     case EL_DARK_YAMYAM:
14857       RaiseScore(level.score[SC_YAMYAM]);
14858       break;
14859     case EL_ROBOT:
14860       RaiseScore(level.score[SC_ROBOT]);
14861       break;
14862     case EL_PACMAN:
14863       RaiseScore(level.score[SC_PACMAN]);
14864       break;
14865     case EL_NUT:
14866       RaiseScore(level.score[SC_NUT]);
14867       break;
14868     case EL_DYNAMITE:
14869     case EL_EM_DYNAMITE:
14870     case EL_SP_DISK_RED:
14871     case EL_DYNABOMB_INCREASE_NUMBER:
14872     case EL_DYNABOMB_INCREASE_SIZE:
14873     case EL_DYNABOMB_INCREASE_POWER:
14874       RaiseScore(level.score[SC_DYNAMITE]);
14875       break;
14876     case EL_SHIELD_NORMAL:
14877     case EL_SHIELD_DEADLY:
14878       RaiseScore(level.score[SC_SHIELD]);
14879       break;
14880     case EL_EXTRA_TIME:
14881       RaiseScore(level.extra_time_score);
14882       break;
14883     case EL_KEY_1:
14884     case EL_KEY_2:
14885     case EL_KEY_3:
14886     case EL_KEY_4:
14887     case EL_EM_KEY_1:
14888     case EL_EM_KEY_2:
14889     case EL_EM_KEY_3:
14890     case EL_EM_KEY_4:
14891     case EL_EMC_KEY_5:
14892     case EL_EMC_KEY_6:
14893     case EL_EMC_KEY_7:
14894     case EL_EMC_KEY_8:
14895     case EL_DC_KEY_WHITE:
14896       RaiseScore(level.score[SC_KEY]);
14897       break;
14898     default:
14899       RaiseScore(element_info[element].collect_score);
14900       break;
14901   }
14902 }
14903
14904 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14905 {
14906   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14907   {
14908     /* closing door required in case of envelope style request dialogs */
14909     if (!skip_request)
14910       CloseDoor(DOOR_CLOSE_1);
14911
14912     if (network.enabled)
14913       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14914     else
14915     {
14916       if (quick_quit)
14917         FadeSkipNextFadeIn();
14918
14919       SetGameStatus(GAME_MODE_MAIN);
14920
14921       DrawMainMenu();
14922     }
14923   }
14924   else          /* continue playing the game */
14925   {
14926     if (tape.playing && tape.deactivate_display)
14927       TapeDeactivateDisplayOff(TRUE);
14928
14929     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14930
14931     if (tape.playing && tape.deactivate_display)
14932       TapeDeactivateDisplayOn();
14933   }
14934 }
14935
14936 void RequestQuitGame(boolean ask_if_really_quit)
14937 {
14938   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14939   boolean skip_request = AllPlayersGone || quick_quit;
14940
14941   RequestQuitGameExt(skip_request, quick_quit,
14942                      "Do you really want to quit the game?");
14943 }
14944
14945 void RequestRestartGame(char *message)
14946 {
14947   game.restart_game_message = NULL;
14948
14949   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14950   {
14951     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14952   }
14953   else
14954   {
14955     SetGameStatus(GAME_MODE_MAIN);
14956
14957     DrawMainMenu();
14958   }
14959 }
14960
14961
14962 /* ------------------------------------------------------------------------- */
14963 /* random generator functions                                                */
14964 /* ------------------------------------------------------------------------- */
14965
14966 unsigned int InitEngineRandom_RND(int seed)
14967 {
14968   game.num_random_calls = 0;
14969
14970   return InitEngineRandom(seed);
14971 }
14972
14973 unsigned int RND(int max)
14974 {
14975   if (max > 0)
14976   {
14977     game.num_random_calls++;
14978
14979     return GetEngineRandom(max);
14980   }
14981
14982   return 0;
14983 }
14984
14985
14986 /* ------------------------------------------------------------------------- */
14987 /* game engine snapshot handling functions                                   */
14988 /* ------------------------------------------------------------------------- */
14989
14990 struct EngineSnapshotInfo
14991 {
14992   /* runtime values for custom element collect score */
14993   int collect_score[NUM_CUSTOM_ELEMENTS];
14994
14995   /* runtime values for group element choice position */
14996   int choice_pos[NUM_GROUP_ELEMENTS];
14997
14998   /* runtime values for belt position animations */
14999   int belt_graphic[4][NUM_BELT_PARTS];
15000   int belt_anim_mode[4][NUM_BELT_PARTS];
15001 };
15002
15003 static struct EngineSnapshotInfo engine_snapshot_rnd;
15004 static char *snapshot_level_identifier = NULL;
15005 static int snapshot_level_nr = -1;
15006
15007 static void SaveEngineSnapshotValues_RND()
15008 {
15009   static int belt_base_active_element[4] =
15010   {
15011     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15012     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15013     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15014     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15015   };
15016   int i, j;
15017
15018   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15019   {
15020     int element = EL_CUSTOM_START + i;
15021
15022     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15023   }
15024
15025   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15026   {
15027     int element = EL_GROUP_START + i;
15028
15029     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15030   }
15031
15032   for (i = 0; i < 4; i++)
15033   {
15034     for (j = 0; j < NUM_BELT_PARTS; j++)
15035     {
15036       int element = belt_base_active_element[i] + j;
15037       int graphic = el2img(element);
15038       int anim_mode = graphic_info[graphic].anim_mode;
15039
15040       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15041       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15042     }
15043   }
15044 }
15045
15046 static void LoadEngineSnapshotValues_RND()
15047 {
15048   unsigned int num_random_calls = game.num_random_calls;
15049   int i, j;
15050
15051   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15052   {
15053     int element = EL_CUSTOM_START + i;
15054
15055     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15056   }
15057
15058   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15059   {
15060     int element = EL_GROUP_START + i;
15061
15062     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15063   }
15064
15065   for (i = 0; i < 4; i++)
15066   {
15067     for (j = 0; j < NUM_BELT_PARTS; j++)
15068     {
15069       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15070       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15071
15072       graphic_info[graphic].anim_mode = anim_mode;
15073     }
15074   }
15075
15076   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15077   {
15078     InitRND(tape.random_seed);
15079     for (i = 0; i < num_random_calls; i++)
15080       RND(1);
15081   }
15082
15083   if (game.num_random_calls != num_random_calls)
15084   {
15085     Error(ERR_INFO, "number of random calls out of sync");
15086     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15087     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15088     Error(ERR_EXIT, "this should not happen -- please debug");
15089   }
15090 }
15091
15092 void FreeEngineSnapshotSingle()
15093 {
15094   FreeSnapshotSingle();
15095
15096   setString(&snapshot_level_identifier, NULL);
15097   snapshot_level_nr = -1;
15098 }
15099
15100 void FreeEngineSnapshotList()
15101 {
15102   FreeSnapshotList();
15103 }
15104
15105 ListNode *SaveEngineSnapshotBuffers()
15106 {
15107   ListNode *buffers = NULL;
15108
15109   /* copy some special values to a structure better suited for the snapshot */
15110
15111   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15112     SaveEngineSnapshotValues_RND();
15113   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15114     SaveEngineSnapshotValues_EM();
15115   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15116     SaveEngineSnapshotValues_SP(&buffers);
15117   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15118     SaveEngineSnapshotValues_MM(&buffers);
15119
15120   /* save values stored in special snapshot structure */
15121
15122   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15123     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15124   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15125     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15126   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15127     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15128   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15129     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15130
15131   /* save further RND engine values */
15132
15133   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15136
15137   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15141
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15147
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15151
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15153
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15155
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15158
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15177
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15180
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15184
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15187
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15193
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15196
15197 #if 0
15198   ListNode *node = engine_snapshot_list_rnd;
15199   int num_bytes = 0;
15200
15201   while (node != NULL)
15202   {
15203     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15204
15205     node = node->next;
15206   }
15207
15208   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15209 #endif
15210
15211   return buffers;
15212 }
15213
15214 void SaveEngineSnapshotSingle()
15215 {
15216   ListNode *buffers = SaveEngineSnapshotBuffers();
15217
15218   /* finally save all snapshot buffers to single snapshot */
15219   SaveSnapshotSingle(buffers);
15220
15221   /* save level identification information */
15222   setString(&snapshot_level_identifier, leveldir_current->identifier);
15223   snapshot_level_nr = level_nr;
15224 }
15225
15226 boolean CheckSaveEngineSnapshotToList()
15227 {
15228   boolean save_snapshot =
15229     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15230      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15231       game.snapshot.changed_action) ||
15232      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15233       game.snapshot.collected_item));
15234
15235   game.snapshot.changed_action = FALSE;
15236   game.snapshot.collected_item = FALSE;
15237   game.snapshot.save_snapshot = save_snapshot;
15238
15239   return save_snapshot;
15240 }
15241
15242 void SaveEngineSnapshotToList()
15243 {
15244   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15245       tape.quick_resume)
15246     return;
15247
15248   ListNode *buffers = SaveEngineSnapshotBuffers();
15249
15250   /* finally save all snapshot buffers to snapshot list */
15251   SaveSnapshotToList(buffers);
15252 }
15253
15254 void SaveEngineSnapshotToListInitial()
15255 {
15256   FreeEngineSnapshotList();
15257
15258   SaveEngineSnapshotToList();
15259 }
15260
15261 void LoadEngineSnapshotValues()
15262 {
15263   /* restore special values from snapshot structure */
15264
15265   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15266     LoadEngineSnapshotValues_RND();
15267   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15268     LoadEngineSnapshotValues_EM();
15269   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15270     LoadEngineSnapshotValues_SP();
15271   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15272     LoadEngineSnapshotValues_MM();
15273 }
15274
15275 void LoadEngineSnapshotSingle()
15276 {
15277   LoadSnapshotSingle();
15278
15279   LoadEngineSnapshotValues();
15280 }
15281
15282 void LoadEngineSnapshot_Undo(int steps)
15283 {
15284   LoadSnapshotFromList_Older(steps);
15285
15286   LoadEngineSnapshotValues();
15287 }
15288
15289 void LoadEngineSnapshot_Redo(int steps)
15290 {
15291   LoadSnapshotFromList_Newer(steps);
15292
15293   LoadEngineSnapshotValues();
15294 }
15295
15296 boolean CheckEngineSnapshotSingle()
15297 {
15298   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15299           snapshot_level_nr == level_nr);
15300 }
15301
15302 boolean CheckEngineSnapshotList()
15303 {
15304   return CheckSnapshotList();
15305 }
15306
15307
15308 /* ---------- new game button stuff ---------------------------------------- */
15309
15310 static struct
15311 {
15312   int graphic;
15313   struct XY *pos;
15314   int gadget_id;
15315   boolean *setup_value;
15316   boolean allowed_on_tape;
15317   char *infotext;
15318 } gamebutton_info[NUM_GAME_BUTTONS] =
15319 {
15320   {
15321     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15322     GAME_CTRL_ID_STOP,                          NULL,
15323     TRUE,                                       "stop game"
15324   },
15325   {
15326     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15327     GAME_CTRL_ID_PAUSE,                         NULL,
15328     TRUE,                                       "pause game"
15329   },
15330   {
15331     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15332     GAME_CTRL_ID_PLAY,                          NULL,
15333     TRUE,                                       "play game"
15334   },
15335   {
15336     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15337     GAME_CTRL_ID_UNDO,                          NULL,
15338     TRUE,                                       "undo step"
15339   },
15340   {
15341     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15342     GAME_CTRL_ID_REDO,                          NULL,
15343     TRUE,                                       "redo step"
15344   },
15345   {
15346     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15347     GAME_CTRL_ID_SAVE,                          NULL,
15348     TRUE,                                       "save game"
15349   },
15350   {
15351     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15352     GAME_CTRL_ID_PAUSE2,                        NULL,
15353     TRUE,                                       "pause game"
15354   },
15355   {
15356     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15357     GAME_CTRL_ID_LOAD,                          NULL,
15358     TRUE,                                       "load game"
15359   },
15360   {
15361     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15362     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15363     FALSE,                                      "stop game"
15364   },
15365   {
15366     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15367     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15368     FALSE,                                      "pause game"
15369   },
15370   {
15371     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15372     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15373     FALSE,                                      "play game"
15374   },
15375   {
15376     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15377     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15378     TRUE,                                       "background music on/off"
15379   },
15380   {
15381     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15382     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15383     TRUE,                                       "sound loops on/off"
15384   },
15385   {
15386     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15387     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15388     TRUE,                                       "normal sounds on/off"
15389   },
15390   {
15391     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15392     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15393     FALSE,                                      "background music on/off"
15394   },
15395   {
15396     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15397     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15398     FALSE,                                      "sound loops on/off"
15399   },
15400   {
15401     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15402     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15403     FALSE,                                      "normal sounds on/off"
15404   }
15405 };
15406
15407 void CreateGameButtons()
15408 {
15409   int i;
15410
15411   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15412   {
15413     int graphic = gamebutton_info[i].graphic;
15414     struct GraphicInfo *gfx = &graphic_info[graphic];
15415     struct XY *pos = gamebutton_info[i].pos;
15416     struct GadgetInfo *gi;
15417     int button_type;
15418     boolean checked;
15419     unsigned int event_mask;
15420     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15421     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15422     int base_x = (on_tape ? VX : DX);
15423     int base_y = (on_tape ? VY : DY);
15424     int gd_x   = gfx->src_x;
15425     int gd_y   = gfx->src_y;
15426     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15427     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15428     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15429     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15430     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15431     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15432     int id = i;
15433
15434     if (gfx->bitmap == NULL)
15435     {
15436       game_gadget[id] = NULL;
15437
15438       continue;
15439     }
15440
15441     if (id == GAME_CTRL_ID_STOP ||
15442         id == GAME_CTRL_ID_PANEL_STOP ||
15443         id == GAME_CTRL_ID_PLAY ||
15444         id == GAME_CTRL_ID_PANEL_PLAY ||
15445         id == GAME_CTRL_ID_SAVE ||
15446         id == GAME_CTRL_ID_LOAD)
15447     {
15448       button_type = GD_TYPE_NORMAL_BUTTON;
15449       checked = FALSE;
15450       event_mask = GD_EVENT_RELEASED;
15451     }
15452     else if (id == GAME_CTRL_ID_UNDO ||
15453              id == GAME_CTRL_ID_REDO)
15454     {
15455       button_type = GD_TYPE_NORMAL_BUTTON;
15456       checked = FALSE;
15457       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15458     }
15459     else
15460     {
15461       button_type = GD_TYPE_CHECK_BUTTON;
15462       checked = (gamebutton_info[i].setup_value != NULL ?
15463                  *gamebutton_info[i].setup_value : FALSE);
15464       event_mask = GD_EVENT_PRESSED;
15465     }
15466
15467     gi = CreateGadget(GDI_CUSTOM_ID, id,
15468                       GDI_IMAGE_ID, graphic,
15469                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15470                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15471                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15472                       GDI_WIDTH, gfx->width,
15473                       GDI_HEIGHT, gfx->height,
15474                       GDI_TYPE, button_type,
15475                       GDI_STATE, GD_BUTTON_UNPRESSED,
15476                       GDI_CHECKED, checked,
15477                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15478                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15479                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15480                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15481                       GDI_DIRECT_DRAW, FALSE,
15482                       GDI_EVENT_MASK, event_mask,
15483                       GDI_CALLBACK_ACTION, HandleGameButtons,
15484                       GDI_END);
15485
15486     if (gi == NULL)
15487       Error(ERR_EXIT, "cannot create gadget");
15488
15489     game_gadget[id] = gi;
15490   }
15491 }
15492
15493 void FreeGameButtons()
15494 {
15495   int i;
15496
15497   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15498     FreeGadget(game_gadget[i]);
15499 }
15500
15501 static void UnmapGameButtonsAtSamePosition(int id)
15502 {
15503   int i;
15504
15505   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15506     if (i != id &&
15507         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15508         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15509       UnmapGadget(game_gadget[i]);
15510 }
15511
15512 static void UnmapGameButtonsAtSamePosition_All()
15513 {
15514   if (setup.show_snapshot_buttons)
15515   {
15516     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15517     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15518     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15519   }
15520   else
15521   {
15522     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15523     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15524     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15525
15526     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15527     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15528     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15529   }
15530 }
15531
15532 static void MapGameButtonsAtSamePosition(int id)
15533 {
15534   int i;
15535
15536   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15537     if (i != id &&
15538         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15539         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15540       MapGadget(game_gadget[i]);
15541
15542   UnmapGameButtonsAtSamePosition_All();
15543 }
15544
15545 void MapUndoRedoButtons()
15546 {
15547   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15548   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15549
15550   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15551   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15552
15553   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15554 }
15555
15556 void UnmapUndoRedoButtons()
15557 {
15558   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15559   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15560
15561   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15562   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15563
15564   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15565 }
15566
15567 void MapGameButtonsExt(boolean on_tape)
15568 {
15569   int i;
15570
15571   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15572     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15573         i != GAME_CTRL_ID_UNDO &&
15574         i != GAME_CTRL_ID_REDO)
15575       MapGadget(game_gadget[i]);
15576
15577   UnmapGameButtonsAtSamePosition_All();
15578
15579   RedrawGameButtons();
15580 }
15581
15582 void UnmapGameButtonsExt(boolean on_tape)
15583 {
15584   int i;
15585
15586   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15587     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15588       UnmapGadget(game_gadget[i]);
15589 }
15590
15591 void RedrawGameButtonsExt(boolean on_tape)
15592 {
15593   int i;
15594
15595   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15596     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15597       RedrawGadget(game_gadget[i]);
15598
15599   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15600   redraw_mask &= ~REDRAW_ALL;
15601 }
15602
15603 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15604 {
15605   if (gi == NULL)
15606     return;
15607
15608   gi->checked = state;
15609 }
15610
15611 void RedrawSoundButtonGadget(int id)
15612 {
15613   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15614              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15615              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15616              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15617              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15618              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15619              id);
15620
15621   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15622   RedrawGadget(game_gadget[id2]);
15623 }
15624
15625 void MapGameButtons()
15626 {
15627   MapGameButtonsExt(FALSE);
15628 }
15629
15630 void UnmapGameButtons()
15631 {
15632   UnmapGameButtonsExt(FALSE);
15633 }
15634
15635 void RedrawGameButtons()
15636 {
15637   RedrawGameButtonsExt(FALSE);
15638 }
15639
15640 void MapGameButtonsOnTape()
15641 {
15642   MapGameButtonsExt(TRUE);
15643 }
15644
15645 void UnmapGameButtonsOnTape()
15646 {
15647   UnmapGameButtonsExt(TRUE);
15648 }
15649
15650 void RedrawGameButtonsOnTape()
15651 {
15652   RedrawGameButtonsExt(TRUE);
15653 }
15654
15655 void GameUndoRedoExt()
15656 {
15657   ClearPlayerAction();
15658
15659   tape.pausing = TRUE;
15660
15661   RedrawPlayfield();
15662   UpdateAndDisplayGameControlValues();
15663
15664   DrawCompleteVideoDisplay();
15665   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15666   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15667   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15668
15669   BackToFront();
15670 }
15671
15672 void GameUndo(int steps)
15673 {
15674   if (!CheckEngineSnapshotList())
15675     return;
15676
15677   LoadEngineSnapshot_Undo(steps);
15678
15679   GameUndoRedoExt();
15680 }
15681
15682 void GameRedo(int steps)
15683 {
15684   if (!CheckEngineSnapshotList())
15685     return;
15686
15687   LoadEngineSnapshot_Redo(steps);
15688
15689   GameUndoRedoExt();
15690 }
15691
15692 static void HandleGameButtonsExt(int id, int button)
15693 {
15694   static boolean game_undo_executed = FALSE;
15695   int steps = BUTTON_STEPSIZE(button);
15696   boolean handle_game_buttons =
15697     (game_status == GAME_MODE_PLAYING ||
15698      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15699
15700   if (!handle_game_buttons)
15701     return;
15702
15703   switch (id)
15704   {
15705     case GAME_CTRL_ID_STOP:
15706     case GAME_CTRL_ID_PANEL_STOP:
15707       if (game_status == GAME_MODE_MAIN)
15708         break;
15709
15710       if (tape.playing)
15711         TapeStop();
15712       else
15713         RequestQuitGame(TRUE);
15714
15715       break;
15716
15717     case GAME_CTRL_ID_PAUSE:
15718     case GAME_CTRL_ID_PAUSE2:
15719     case GAME_CTRL_ID_PANEL_PAUSE:
15720       if (network.enabled && game_status == GAME_MODE_PLAYING)
15721       {
15722         if (tape.pausing)
15723           SendToServer_ContinuePlaying();
15724         else
15725           SendToServer_PausePlaying();
15726       }
15727       else
15728         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15729
15730       game_undo_executed = FALSE;
15731
15732       break;
15733
15734     case GAME_CTRL_ID_PLAY:
15735     case GAME_CTRL_ID_PANEL_PLAY:
15736       if (game_status == GAME_MODE_MAIN)
15737       {
15738         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15739       }
15740       else if (tape.pausing)
15741       {
15742         if (network.enabled)
15743           SendToServer_ContinuePlaying();
15744         else
15745           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15746       }
15747       break;
15748
15749     case GAME_CTRL_ID_UNDO:
15750       // Important: When using "save snapshot when collecting an item" mode,
15751       // load last (current) snapshot for first "undo" after pressing "pause"
15752       // (else the last-but-one snapshot would be loaded, because the snapshot
15753       // pointer already points to the last snapshot when pressing "pause",
15754       // which is fine for "every step/move" mode, but not for "every collect")
15755       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15756           !game_undo_executed)
15757         steps--;
15758
15759       game_undo_executed = TRUE;
15760
15761       GameUndo(steps);
15762       break;
15763
15764     case GAME_CTRL_ID_REDO:
15765       GameRedo(steps);
15766       break;
15767
15768     case GAME_CTRL_ID_SAVE:
15769       TapeQuickSave();
15770       break;
15771
15772     case GAME_CTRL_ID_LOAD:
15773       TapeQuickLoad();
15774       break;
15775
15776     case SOUND_CTRL_ID_MUSIC:
15777     case SOUND_CTRL_ID_PANEL_MUSIC:
15778       if (setup.sound_music)
15779       { 
15780         setup.sound_music = FALSE;
15781
15782         FadeMusic();
15783       }
15784       else if (audio.music_available)
15785       { 
15786         setup.sound = setup.sound_music = TRUE;
15787
15788         SetAudioMode(setup.sound);
15789
15790         if (game_status == GAME_MODE_PLAYING)
15791           PlayLevelMusic();
15792       }
15793
15794       RedrawSoundButtonGadget(id);
15795
15796       break;
15797
15798     case SOUND_CTRL_ID_LOOPS:
15799     case SOUND_CTRL_ID_PANEL_LOOPS:
15800       if (setup.sound_loops)
15801         setup.sound_loops = FALSE;
15802       else if (audio.loops_available)
15803       {
15804         setup.sound = setup.sound_loops = TRUE;
15805
15806         SetAudioMode(setup.sound);
15807       }
15808
15809       RedrawSoundButtonGadget(id);
15810
15811       break;
15812
15813     case SOUND_CTRL_ID_SIMPLE:
15814     case SOUND_CTRL_ID_PANEL_SIMPLE:
15815       if (setup.sound_simple)
15816         setup.sound_simple = FALSE;
15817       else if (audio.sound_available)
15818       {
15819         setup.sound = setup.sound_simple = TRUE;
15820
15821         SetAudioMode(setup.sound);
15822       }
15823
15824       RedrawSoundButtonGadget(id);
15825
15826       break;
15827
15828     default:
15829       break;
15830   }
15831 }
15832
15833 static void HandleGameButtons(struct GadgetInfo *gi)
15834 {
15835   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15836 }
15837
15838 void HandleSoundButtonKeys(Key key)
15839 {
15840   if (key == setup.shortcut.sound_simple)
15841     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15842   else if (key == setup.shortcut.sound_loops)
15843     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15844   else if (key == setup.shortcut.sound_music)
15845     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15846 }