fixed unexpected behaviour with solving levels with Sokoban elements
[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()             (game.panel.active == FALSE)
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(void);
1089 static void FadeLevelSoundsAndMusic(void);
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(void)
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(void)
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(void)
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->sokoban_fields_still_needed++;
1796       break;
1797
1798     case EL_SOKOBAN_OBJECT:
1799       local_player->sokoban_objects_still_needed++;
1800       break;
1801
1802     case EL_STONEBLOCK:
1803       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1804         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1805       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1809       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1810         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1811       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1812         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1813       break;
1814
1815     case EL_BUG:
1816     case EL_BUG_RIGHT:
1817     case EL_BUG_UP:
1818     case EL_BUG_LEFT:
1819     case EL_BUG_DOWN:
1820     case EL_SPACESHIP:
1821     case EL_SPACESHIP_RIGHT:
1822     case EL_SPACESHIP_UP:
1823     case EL_SPACESHIP_LEFT:
1824     case EL_SPACESHIP_DOWN:
1825     case EL_BD_BUTTERFLY:
1826     case EL_BD_BUTTERFLY_RIGHT:
1827     case EL_BD_BUTTERFLY_UP:
1828     case EL_BD_BUTTERFLY_LEFT:
1829     case EL_BD_BUTTERFLY_DOWN:
1830     case EL_BD_FIREFLY:
1831     case EL_BD_FIREFLY_RIGHT:
1832     case EL_BD_FIREFLY_UP:
1833     case EL_BD_FIREFLY_LEFT:
1834     case EL_BD_FIREFLY_DOWN:
1835     case EL_PACMAN_RIGHT:
1836     case EL_PACMAN_UP:
1837     case EL_PACMAN_LEFT:
1838     case EL_PACMAN_DOWN:
1839     case EL_YAMYAM:
1840     case EL_YAMYAM_LEFT:
1841     case EL_YAMYAM_RIGHT:
1842     case EL_YAMYAM_UP:
1843     case EL_YAMYAM_DOWN:
1844     case EL_DARK_YAMYAM:
1845     case EL_ROBOT:
1846     case EL_PACMAN:
1847     case EL_SP_SNIKSNAK:
1848     case EL_SP_ELECTRON:
1849     case EL_MOLE:
1850     case EL_MOLE_LEFT:
1851     case EL_MOLE_RIGHT:
1852     case EL_MOLE_UP:
1853     case EL_MOLE_DOWN:
1854       InitMovDir(x, y);
1855       break;
1856
1857     case EL_AMOEBA_FULL:
1858     case EL_BD_AMOEBA:
1859       InitAmoebaNr(x, y);
1860       break;
1861
1862     case EL_AMOEBA_DROP:
1863       if (y == lev_fieldy - 1)
1864       {
1865         Feld[x][y] = EL_AMOEBA_GROWING;
1866         Store[x][y] = EL_AMOEBA_WET;
1867       }
1868       break;
1869
1870     case EL_DYNAMITE_ACTIVE:
1871     case EL_SP_DISK_RED_ACTIVE:
1872     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1873     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1874     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1875     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1876       MovDelay[x][y] = 96;
1877       break;
1878
1879     case EL_EM_DYNAMITE_ACTIVE:
1880       MovDelay[x][y] = 32;
1881       break;
1882
1883     case EL_LAMP:
1884       local_player->lights_still_needed++;
1885       break;
1886
1887     case EL_PENGUIN:
1888       local_player->friends_still_needed++;
1889       break;
1890
1891     case EL_PIG:
1892     case EL_DRAGON:
1893       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1894       break;
1895
1896     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1897     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1898     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1899     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1900     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1901     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1902     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1903     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1904     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1905     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1906     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1907     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1908       if (init_game)
1909       {
1910         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1911         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1912         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1913
1914         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1915         {
1916           game.belt_dir[belt_nr] = belt_dir;
1917           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1918         }
1919         else    // more than one switch -- set it like the first switch
1920         {
1921           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1922         }
1923       }
1924       break;
1925
1926     case EL_LIGHT_SWITCH_ACTIVE:
1927       if (init_game)
1928         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1929       break;
1930
1931     case EL_INVISIBLE_STEELWALL:
1932     case EL_INVISIBLE_WALL:
1933     case EL_INVISIBLE_SAND:
1934       if (game.light_time_left > 0 ||
1935           game.lenses_time_left > 0)
1936         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1942       break;
1943
1944     case EL_EMC_MAGIC_BALL_SWITCH:
1945       if (game.ball_state)
1946         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1947       break;
1948
1949     case EL_TRIGGER_PLAYER:
1950     case EL_TRIGGER_ELEMENT:
1951     case EL_TRIGGER_CE_VALUE:
1952     case EL_TRIGGER_CE_SCORE:
1953     case EL_SELF:
1954     case EL_ANY_ELEMENT:
1955     case EL_CURRENT_CE_VALUE:
1956     case EL_CURRENT_CE_SCORE:
1957     case EL_PREV_CE_1:
1958     case EL_PREV_CE_2:
1959     case EL_PREV_CE_3:
1960     case EL_PREV_CE_4:
1961     case EL_PREV_CE_5:
1962     case EL_PREV_CE_6:
1963     case EL_PREV_CE_7:
1964     case EL_PREV_CE_8:
1965     case EL_NEXT_CE_1:
1966     case EL_NEXT_CE_2:
1967     case EL_NEXT_CE_3:
1968     case EL_NEXT_CE_4:
1969     case EL_NEXT_CE_5:
1970     case EL_NEXT_CE_6:
1971     case EL_NEXT_CE_7:
1972     case EL_NEXT_CE_8:
1973       // reference elements should not be used on the playfield
1974       Feld[x][y] = EL_EMPTY;
1975       break;
1976
1977     default:
1978       if (IS_CUSTOM_ELEMENT(element))
1979       {
1980         if (CAN_MOVE(element))
1981           InitMovDir(x, y);
1982
1983         if (!element_info[element].use_last_ce_value || init_game)
1984           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1985       }
1986       else if (IS_GROUP_ELEMENT(element))
1987       {
1988         Feld[x][y] = GetElementFromGroupElement(element);
1989
1990         InitField(x, y, init_game);
1991       }
1992
1993       break;
1994   }
1995
1996   if (!init_game)
1997     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1998 }
1999
2000 static void InitField_WithBug1(int x, int y, boolean init_game)
2001 {
2002   InitField(x, y, init_game);
2003
2004   // not needed to call InitMovDir() -- already done by InitField()!
2005   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2006       CAN_MOVE(Feld[x][y]))
2007     InitMovDir(x, y);
2008 }
2009
2010 static void InitField_WithBug2(int x, int y, boolean init_game)
2011 {
2012   int old_element = Feld[x][y];
2013
2014   InitField(x, y, init_game);
2015
2016   // not needed to call InitMovDir() -- already done by InitField()!
2017   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2018       CAN_MOVE(old_element) &&
2019       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2020     InitMovDir(x, y);
2021
2022   /* this case is in fact a combination of not less than three bugs:
2023      first, it calls InitMovDir() for elements that can move, although this is
2024      already done by InitField(); then, it checks the element that was at this
2025      field _before_ the call to InitField() (which can change it); lastly, it
2026      was not called for "mole with direction" elements, which were treated as
2027      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2028   */
2029 }
2030
2031 static int get_key_element_from_nr(int key_nr)
2032 {
2033   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2034                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2035                           EL_EM_KEY_1 : EL_KEY_1);
2036
2037   return key_base_element + key_nr;
2038 }
2039
2040 static int get_next_dropped_element(struct PlayerInfo *player)
2041 {
2042   return (player->inventory_size > 0 ?
2043           player->inventory_element[player->inventory_size - 1] :
2044           player->inventory_infinite_element != EL_UNDEFINED ?
2045           player->inventory_infinite_element :
2046           player->dynabombs_left > 0 ?
2047           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2048           EL_UNDEFINED);
2049 }
2050
2051 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2052 {
2053   // pos >= 0: get element from bottom of the stack;
2054   // pos <  0: get element from top of the stack
2055
2056   if (pos < 0)
2057   {
2058     int min_inventory_size = -pos;
2059     int inventory_pos = player->inventory_size - min_inventory_size;
2060     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2061
2062     return (player->inventory_size >= min_inventory_size ?
2063             player->inventory_element[inventory_pos] :
2064             player->inventory_infinite_element != EL_UNDEFINED ?
2065             player->inventory_infinite_element :
2066             player->dynabombs_left >= min_dynabombs_left ?
2067             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2068             EL_UNDEFINED);
2069   }
2070   else
2071   {
2072     int min_dynabombs_left = pos + 1;
2073     int min_inventory_size = pos + 1 - player->dynabombs_left;
2074     int inventory_pos = pos - player->dynabombs_left;
2075
2076     return (player->inventory_infinite_element != EL_UNDEFINED ?
2077             player->inventory_infinite_element :
2078             player->dynabombs_left >= min_dynabombs_left ?
2079             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2080             player->inventory_size >= min_inventory_size ?
2081             player->inventory_element[inventory_pos] :
2082             EL_UNDEFINED);
2083   }
2084 }
2085
2086 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2087 {
2088   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2089   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2090   int compare_result;
2091
2092   if (gpo1->sort_priority != gpo2->sort_priority)
2093     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2094   else
2095     compare_result = gpo1->nr - gpo2->nr;
2096
2097   return compare_result;
2098 }
2099
2100 int getPlayerInventorySize(int player_nr)
2101 {
2102   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2103     return level.native_em_level->ply[player_nr]->dynamite;
2104   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2105     return level.native_sp_level->game_sp->red_disk_count;
2106   else
2107     return stored_player[player_nr].inventory_size;
2108 }
2109
2110 static void InitGameControlValues(void)
2111 {
2112   int i;
2113
2114   for (i = 0; game_panel_controls[i].nr != -1; i++)
2115   {
2116     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2117     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2118     struct TextPosInfo *pos = gpc->pos;
2119     int nr = gpc->nr;
2120     int type = gpc->type;
2121
2122     if (nr != i)
2123     {
2124       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2125       Error(ERR_EXIT, "this should not happen -- please debug");
2126     }
2127
2128     // force update of game controls after initialization
2129     gpc->value = gpc->last_value = -1;
2130     gpc->frame = gpc->last_frame = -1;
2131     gpc->gfx_frame = -1;
2132
2133     // determine panel value width for later calculation of alignment
2134     if (type == TYPE_INTEGER || type == TYPE_STRING)
2135     {
2136       pos->width = pos->size * getFontWidth(pos->font);
2137       pos->height = getFontHeight(pos->font);
2138     }
2139     else if (type == TYPE_ELEMENT)
2140     {
2141       pos->width = pos->size;
2142       pos->height = pos->size;
2143     }
2144
2145     // fill structure for game panel draw order
2146     gpo->nr = gpc->nr;
2147     gpo->sort_priority = pos->sort_priority;
2148   }
2149
2150   // sort game panel controls according to sort_priority and control number
2151   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2152         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2153 }
2154
2155 static void UpdatePlayfieldElementCount(void)
2156 {
2157   boolean use_element_count = FALSE;
2158   int i, j, x, y;
2159
2160   // first check if it is needed at all to calculate playfield element count
2161   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2162     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2163       use_element_count = TRUE;
2164
2165   if (!use_element_count)
2166     return;
2167
2168   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2169     element_info[i].element_count = 0;
2170
2171   SCAN_PLAYFIELD(x, y)
2172   {
2173     element_info[Feld[x][y]].element_count++;
2174   }
2175
2176   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2177     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2178       if (IS_IN_GROUP(j, i))
2179         element_info[EL_GROUP_START + i].element_count +=
2180           element_info[j].element_count;
2181 }
2182
2183 static void UpdateGameControlValues(void)
2184 {
2185   int i, k;
2186   int time = (local_player->LevelSolved ?
2187               local_player->LevelSolved_CountingTime :
2188               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2189               level.native_em_level->lev->time :
2190               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2191               level.native_sp_level->game_sp->time_played :
2192               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2193               game_mm.energy_left :
2194               game.no_time_limit ? TimePlayed : TimeLeft);
2195   int score = (local_player->LevelSolved ?
2196                local_player->LevelSolved_CountingScore :
2197                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2198                level.native_em_level->lev->score :
2199                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2200                level.native_sp_level->game_sp->score :
2201                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2202                game_mm.score :
2203                local_player->score);
2204   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2205               level.native_em_level->lev->required :
2206               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2207               level.native_sp_level->game_sp->infotrons_still_needed :
2208               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2209               game_mm.kettles_still_needed :
2210               local_player->gems_still_needed);
2211   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212                      level.native_em_level->lev->required > 0 :
2213                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2214                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2215                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2216                      game_mm.kettles_still_needed > 0 ||
2217                      game_mm.lights_still_needed > 0 :
2218                      local_player->gems_still_needed > 0 ||
2219                      local_player->sokoban_fields_still_needed > 0 ||
2220                      local_player->sokoban_objects_still_needed > 0 ||
2221                      local_player->lights_still_needed > 0);
2222   int health = (local_player->LevelSolved ?
2223                 local_player->LevelSolved_CountingHealth :
2224                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2225                 MM_HEALTH(game_mm.laser_overload_value) :
2226                 local_player->health);
2227
2228   UpdatePlayfieldElementCount();
2229
2230   // update game panel control values
2231
2232   // used instead of "level_nr" (for network games)
2233   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2234   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2235
2236   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2237   for (i = 0; i < MAX_NUM_KEYS; i++)
2238     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2239   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2240   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2241
2242   if (game.centered_player_nr == -1)
2243   {
2244     for (i = 0; i < MAX_PLAYERS; i++)
2245     {
2246       // only one player in Supaplex game engine
2247       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2248         break;
2249
2250       for (k = 0; k < MAX_NUM_KEYS; k++)
2251       {
2252         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2253         {
2254           if (level.native_em_level->ply[i]->keys & (1 << k))
2255             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2256               get_key_element_from_nr(k);
2257         }
2258         else if (stored_player[i].key[k])
2259           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2260             get_key_element_from_nr(k);
2261       }
2262
2263       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2264         getPlayerInventorySize(i);
2265
2266       if (stored_player[i].num_white_keys > 0)
2267         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2268           EL_DC_KEY_WHITE;
2269
2270       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2271         stored_player[i].num_white_keys;
2272     }
2273   }
2274   else
2275   {
2276     int player_nr = game.centered_player_nr;
2277
2278     for (k = 0; k < MAX_NUM_KEYS; k++)
2279     {
2280       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281       {
2282         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286       else if (stored_player[player_nr].key[k])
2287         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2288           get_key_element_from_nr(k);
2289     }
2290
2291     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292       getPlayerInventorySize(player_nr);
2293
2294     if (stored_player[player_nr].num_white_keys > 0)
2295       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2296
2297     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2298       stored_player[player_nr].num_white_keys;
2299   }
2300
2301   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2302   {
2303     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2304       get_inventory_element_from_pos(local_player, i);
2305     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2306       get_inventory_element_from_pos(local_player, -i - 1);
2307   }
2308
2309   game_panel_controls[GAME_PANEL_SCORE].value = score;
2310   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2311
2312   game_panel_controls[GAME_PANEL_TIME].value = time;
2313
2314   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2315   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2316   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2317
2318   if (level.time == 0)
2319     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2320   else
2321     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2322
2323   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2324   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2325
2326   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2327
2328   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2329     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2332     local_player->shield_normal_time_left;
2333   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2334     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2335      EL_EMPTY);
2336   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2337     local_player->shield_deadly_time_left;
2338
2339   game_panel_controls[GAME_PANEL_EXIT].value =
2340     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2341
2342   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2343     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2344   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2345     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2346      EL_EMC_MAGIC_BALL_SWITCH);
2347
2348   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2349     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2350   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2351     game.light_time_left;
2352
2353   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2354     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2355   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2356     game.timegate_time_left;
2357
2358   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2359     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2360
2361   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2362     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2364     game.lenses_time_left;
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2367     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2368   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2369     game.magnify_time_left;
2370
2371   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2372     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2373      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2374      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2375      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2376      EL_BALLOON_SWITCH_NONE);
2377
2378   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2379     local_player->dynabomb_count;
2380   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2381     local_player->dynabomb_size;
2382   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2383     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2384
2385   game_panel_controls[GAME_PANEL_PENGUINS].value =
2386     local_player->friends_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2389     local_player->sokoban_objects_still_needed;
2390   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2391     local_player->sokoban_fields_still_needed;
2392
2393   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2394     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2395
2396   for (i = 0; i < NUM_BELTS; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2399       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2400        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2401     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2402       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2403   }
2404
2405   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2406     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2407   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2408     game.magic_wall_time_left;
2409
2410   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2411     local_player->gravity;
2412
2413   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2414     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2418       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2419        game.panel.element[i].id : EL_UNDEFINED);
2420
2421   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2422     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2423       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2424        element_info[game.panel.element_count[i].id].element_count : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2429        element_info[game.panel.ce_score[i].id].collect_score : 0);
2430
2431   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2432     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2433       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2434        element_info[game.panel.ce_score_element[i].id].collect_score :
2435        EL_UNDEFINED);
2436
2437   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2438   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2439   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2440
2441   // update game panel control frames
2442
2443   for (i = 0; game_panel_controls[i].nr != -1; i++)
2444   {
2445     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2446
2447     if (gpc->type == TYPE_ELEMENT)
2448     {
2449       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2450       {
2451         int last_anim_random_frame = gfx.anim_random_frame;
2452         int element = gpc->value;
2453         int graphic = el2panelimg(element);
2454
2455         if (gpc->value != gpc->last_value)
2456         {
2457           gpc->gfx_frame = 0;
2458           gpc->gfx_random = INIT_GFX_RANDOM();
2459         }
2460         else
2461         {
2462           gpc->gfx_frame++;
2463
2464           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2465               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2466             gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468
2469         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2470           gfx.anim_random_frame = gpc->gfx_random;
2471
2472         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2473           gpc->gfx_frame = element_info[element].collect_score;
2474
2475         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2476                                               gpc->gfx_frame);
2477
2478         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2479           gfx.anim_random_frame = last_anim_random_frame;
2480       }
2481     }
2482     else if (gpc->type == TYPE_GRAPHIC)
2483     {
2484       if (gpc->graphic != IMG_UNDEFINED)
2485       {
2486         int last_anim_random_frame = gfx.anim_random_frame;
2487         int graphic = gpc->graphic;
2488
2489         if (gpc->value != gpc->last_value)
2490         {
2491           gpc->gfx_frame = 0;
2492           gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494         else
2495         {
2496           gpc->gfx_frame++;
2497
2498           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500             gpc->gfx_random = INIT_GFX_RANDOM();
2501         }
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = gpc->gfx_random;
2505
2506         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2507
2508         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2509           gfx.anim_random_frame = last_anim_random_frame;
2510       }
2511     }
2512   }
2513 }
2514
2515 static void DisplayGameControlValues(void)
2516 {
2517   boolean redraw_panel = FALSE;
2518   int i;
2519
2520   for (i = 0; game_panel_controls[i].nr != -1; i++)
2521   {
2522     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2523
2524     if (PANEL_DEACTIVATED(gpc->pos))
2525       continue;
2526
2527     if (gpc->value == gpc->last_value &&
2528         gpc->frame == gpc->last_frame)
2529       continue;
2530
2531     redraw_panel = TRUE;
2532   }
2533
2534   if (!redraw_panel)
2535     return;
2536
2537   // copy default game door content to main double buffer
2538
2539   // !!! CHECK AGAIN !!!
2540   SetPanelBackground();
2541   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2542   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2543
2544   // redraw game control buttons
2545   RedrawGameButtons();
2546
2547   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2548
2549   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2550   {
2551     int nr = game_panel_order[i].nr;
2552     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2553     struct TextPosInfo *pos = gpc->pos;
2554     int type = gpc->type;
2555     int value = gpc->value;
2556     int frame = gpc->frame;
2557     int size = pos->size;
2558     int font = pos->font;
2559     boolean draw_masked = pos->draw_masked;
2560     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2561
2562     if (PANEL_DEACTIVATED(pos))
2563       continue;
2564
2565     gpc->last_value = value;
2566     gpc->last_frame = frame;
2567
2568     if (type == TYPE_INTEGER)
2569     {
2570       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2571           nr == GAME_PANEL_TIME)
2572       {
2573         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2574
2575         if (use_dynamic_size)           // use dynamic number of digits
2576         {
2577           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2578           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2579           int size2 = size1 + 1;
2580           int font1 = pos->font;
2581           int font2 = pos->font_alt;
2582
2583           size = (value < value_change ? size1 : size2);
2584           font = (value < value_change ? font1 : font2);
2585         }
2586       }
2587
2588       // correct text size if "digits" is zero or less
2589       if (size <= 0)
2590         size = strlen(int2str(value, size));
2591
2592       // dynamically correct text alignment
2593       pos->width = size * getFontWidth(font);
2594
2595       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2596                   int2str(value, size), font, mask_mode);
2597     }
2598     else if (type == TYPE_ELEMENT)
2599     {
2600       int element, graphic;
2601       Bitmap *src_bitmap;
2602       int src_x, src_y;
2603       int width, height;
2604       int dst_x = PANEL_XPOS(pos);
2605       int dst_y = PANEL_YPOS(pos);
2606
2607       if (value != EL_UNDEFINED && value != EL_EMPTY)
2608       {
2609         element = value;
2610         graphic = el2panelimg(value);
2611
2612         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2613
2614         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2615           size = TILESIZE;
2616
2617         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2618                               &src_x, &src_y);
2619
2620         width  = graphic_info[graphic].width  * size / TILESIZE;
2621         height = graphic_info[graphic].height * size / TILESIZE;
2622
2623         if (draw_masked)
2624           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2625                            dst_x, dst_y);
2626         else
2627           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2628                      dst_x, dst_y);
2629       }
2630     }
2631     else if (type == TYPE_GRAPHIC)
2632     {
2633       int graphic        = gpc->graphic;
2634       int graphic_active = gpc->graphic_active;
2635       Bitmap *src_bitmap;
2636       int src_x, src_y;
2637       int width, height;
2638       int dst_x = PANEL_XPOS(pos);
2639       int dst_y = PANEL_YPOS(pos);
2640       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2641                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2642
2643       if (graphic != IMG_UNDEFINED && !skip)
2644       {
2645         if (pos->style == STYLE_REVERSE)
2646           value = 100 - value;
2647
2648         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2649
2650         if (pos->direction & MV_HORIZONTAL)
2651         {
2652           width  = graphic_info[graphic_active].width * value / 100;
2653           height = graphic_info[graphic_active].height;
2654
2655           if (pos->direction == MV_LEFT)
2656           {
2657             src_x += graphic_info[graphic_active].width - width;
2658             dst_x += graphic_info[graphic_active].width - width;
2659           }
2660         }
2661         else
2662         {
2663           width  = graphic_info[graphic_active].width;
2664           height = graphic_info[graphic_active].height * value / 100;
2665
2666           if (pos->direction == MV_UP)
2667           {
2668             src_y += graphic_info[graphic_active].height - height;
2669             dst_y += graphic_info[graphic_active].height - height;
2670           }
2671         }
2672
2673         if (draw_masked)
2674           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2675                            dst_x, dst_y);
2676         else
2677           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2678                      dst_x, dst_y);
2679
2680         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2681
2682         if (pos->direction & MV_HORIZONTAL)
2683         {
2684           if (pos->direction == MV_RIGHT)
2685           {
2686             src_x += width;
2687             dst_x += width;
2688           }
2689           else
2690           {
2691             dst_x = PANEL_XPOS(pos);
2692           }
2693
2694           width = graphic_info[graphic].width - width;
2695         }
2696         else
2697         {
2698           if (pos->direction == MV_DOWN)
2699           {
2700             src_y += height;
2701             dst_y += height;
2702           }
2703           else
2704           {
2705             dst_y = PANEL_YPOS(pos);
2706           }
2707
2708           height = graphic_info[graphic].height - height;
2709         }
2710
2711         if (draw_masked)
2712           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2713                            dst_x, dst_y);
2714         else
2715           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2716                      dst_x, dst_y);
2717       }
2718     }
2719     else if (type == TYPE_STRING)
2720     {
2721       boolean active = (value != 0);
2722       char *state_normal = "off";
2723       char *state_active = "on";
2724       char *state = (active ? state_active : state_normal);
2725       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2726                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2727                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2728                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2729
2730       if (nr == GAME_PANEL_GRAVITY_STATE)
2731       {
2732         int font1 = pos->font;          // (used for normal state)
2733         int font2 = pos->font_alt;      // (used for active state)
2734
2735         font = (active ? font2 : font1);
2736       }
2737
2738       if (s != NULL)
2739       {
2740         char *s_cut;
2741
2742         if (size <= 0)
2743         {
2744           // don't truncate output if "chars" is zero or less
2745           size = strlen(s);
2746
2747           // dynamically correct text alignment
2748           pos->width = size * getFontWidth(font);
2749         }
2750
2751         s_cut = getStringCopyN(s, size);
2752
2753         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2754                     s_cut, font, mask_mode);
2755
2756         free(s_cut);
2757       }
2758     }
2759
2760     redraw_mask |= REDRAW_DOOR_1;
2761   }
2762
2763   SetGameStatus(GAME_MODE_PLAYING);
2764 }
2765
2766 void UpdateAndDisplayGameControlValues(void)
2767 {
2768   if (tape.deactivate_display)
2769     return;
2770
2771   UpdateGameControlValues();
2772   DisplayGameControlValues();
2773 }
2774
2775 #if 0
2776 static void UpdateGameDoorValues(void)
2777 {
2778   UpdateGameControlValues();
2779 }
2780 #endif
2781
2782 void DrawGameDoorValues(void)
2783 {
2784   DisplayGameControlValues();
2785 }
2786
2787
2788 // ============================================================================
2789 // InitGameEngine()
2790 // ----------------------------------------------------------------------------
2791 // initialize game engine due to level / tape version number
2792 // ============================================================================
2793
2794 static void InitGameEngine(void)
2795 {
2796   int i, j, k, l, x, y;
2797
2798   // set game engine from tape file when re-playing, else from level file
2799   game.engine_version = (tape.playing ? tape.engine_version :
2800                          level.game_version);
2801
2802   // set single or multi-player game mode (needed for re-playing tapes)
2803   game.team_mode = setup.team_mode;
2804
2805   if (tape.playing)
2806   {
2807     int num_players = 0;
2808
2809     for (i = 0; i < MAX_PLAYERS; i++)
2810       if (tape.player_participates[i])
2811         num_players++;
2812
2813     // multi-player tapes contain input data for more than one player
2814     game.team_mode = (num_players > 1);
2815   }
2816
2817   // --------------------------------------------------------------------------
2818   // set flags for bugs and changes according to active game engine version
2819   // --------------------------------------------------------------------------
2820
2821   /*
2822     Summary of bugfix/change:
2823     Fixed handling for custom elements that change when pushed by the player.
2824
2825     Fixed/changed in version:
2826     3.1.0
2827
2828     Description:
2829     Before 3.1.0, custom elements that "change when pushing" changed directly
2830     after the player started pushing them (until then handled in "DigField()").
2831     Since 3.1.0, these custom elements are not changed until the "pushing"
2832     move of the element is finished (now handled in "ContinueMoving()").
2833
2834     Affected levels/tapes:
2835     The first condition is generally needed for all levels/tapes before version
2836     3.1.0, which might use the old behaviour before it was changed; known tapes
2837     that are affected are some tapes from the level set "Walpurgis Gardens" by
2838     Jamie Cullen.
2839     The second condition is an exception from the above case and is needed for
2840     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2841     above (including some development versions of 3.1.0), but before it was
2842     known that this change would break tapes like the above and was fixed in
2843     3.1.1, so that the changed behaviour was active although the engine version
2844     while recording maybe was before 3.1.0. There is at least one tape that is
2845     affected by this exception, which is the tape for the one-level set "Bug
2846     Machine" by Juergen Bonhagen.
2847   */
2848
2849   game.use_change_when_pushing_bug =
2850     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2851      !(tape.playing &&
2852        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2853        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2854
2855   /*
2856     Summary of bugfix/change:
2857     Fixed handling for blocking the field the player leaves when moving.
2858
2859     Fixed/changed in version:
2860     3.1.1
2861
2862     Description:
2863     Before 3.1.1, when "block last field when moving" was enabled, the field
2864     the player is leaving when moving was blocked for the time of the move,
2865     and was directly unblocked afterwards. This resulted in the last field
2866     being blocked for exactly one less than the number of frames of one player
2867     move. Additionally, even when blocking was disabled, the last field was
2868     blocked for exactly one frame.
2869     Since 3.1.1, due to changes in player movement handling, the last field
2870     is not blocked at all when blocking is disabled. When blocking is enabled,
2871     the last field is blocked for exactly the number of frames of one player
2872     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2873     last field is blocked for exactly one more than the number of frames of
2874     one player move.
2875
2876     Affected levels/tapes:
2877     (!!! yet to be determined -- probably many !!!)
2878   */
2879
2880   game.use_block_last_field_bug =
2881     (game.engine_version < VERSION_IDENT(3,1,1,0));
2882
2883   game_em.use_single_button =
2884     (game.engine_version > VERSION_IDENT(4,0,0,2));
2885
2886   game_em.use_snap_key_bug =
2887     (game.engine_version < VERSION_IDENT(4,0,1,0));
2888
2889   // --------------------------------------------------------------------------
2890
2891   // set maximal allowed number of custom element changes per game frame
2892   game.max_num_changes_per_frame = 1;
2893
2894   // default scan direction: scan playfield from top/left to bottom/right
2895   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2896
2897   // dynamically adjust element properties according to game engine version
2898   InitElementPropertiesEngine(game.engine_version);
2899
2900 #if 0
2901   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2902   printf("          tape version == %06d [%s] [file: %06d]\n",
2903          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2904          tape.file_version);
2905   printf("       => game.engine_version == %06d\n", game.engine_version);
2906 #endif
2907
2908   // ---------- initialize player's initial move delay ------------------------
2909
2910   // dynamically adjust player properties according to level information
2911   for (i = 0; i < MAX_PLAYERS; i++)
2912     game.initial_move_delay_value[i] =
2913       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2914
2915   // dynamically adjust player properties according to game engine version
2916   for (i = 0; i < MAX_PLAYERS; i++)
2917     game.initial_move_delay[i] =
2918       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2919        game.initial_move_delay_value[i] : 0);
2920
2921   // ---------- initialize player's initial push delay ------------------------
2922
2923   // dynamically adjust player properties according to game engine version
2924   game.initial_push_delay_value =
2925     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2926
2927   // ---------- initialize changing elements ----------------------------------
2928
2929   // initialize changing elements information
2930   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2931   {
2932     struct ElementInfo *ei = &element_info[i];
2933
2934     // this pointer might have been changed in the level editor
2935     ei->change = &ei->change_page[0];
2936
2937     if (!IS_CUSTOM_ELEMENT(i))
2938     {
2939       ei->change->target_element = EL_EMPTY_SPACE;
2940       ei->change->delay_fixed = 0;
2941       ei->change->delay_random = 0;
2942       ei->change->delay_frames = 1;
2943     }
2944
2945     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2946     {
2947       ei->has_change_event[j] = FALSE;
2948
2949       ei->event_page_nr[j] = 0;
2950       ei->event_page[j] = &ei->change_page[0];
2951     }
2952   }
2953
2954   // add changing elements from pre-defined list
2955   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2956   {
2957     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2958     struct ElementInfo *ei = &element_info[ch_delay->element];
2959
2960     ei->change->target_element       = ch_delay->target_element;
2961     ei->change->delay_fixed          = ch_delay->change_delay;
2962
2963     ei->change->pre_change_function  = ch_delay->pre_change_function;
2964     ei->change->change_function      = ch_delay->change_function;
2965     ei->change->post_change_function = ch_delay->post_change_function;
2966
2967     ei->change->can_change = TRUE;
2968     ei->change->can_change_or_has_action = TRUE;
2969
2970     ei->has_change_event[CE_DELAY] = TRUE;
2971
2972     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2973     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2974   }
2975
2976   // ---------- initialize internal run-time variables ------------------------
2977
2978   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2979   {
2980     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2981
2982     for (j = 0; j < ei->num_change_pages; j++)
2983     {
2984       ei->change_page[j].can_change_or_has_action =
2985         (ei->change_page[j].can_change |
2986          ei->change_page[j].has_action);
2987     }
2988   }
2989
2990   // add change events from custom element configuration
2991   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2992   {
2993     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2994
2995     for (j = 0; j < ei->num_change_pages; j++)
2996     {
2997       if (!ei->change_page[j].can_change_or_has_action)
2998         continue;
2999
3000       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3001       {
3002         // only add event page for the first page found with this event
3003         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3004         {
3005           ei->has_change_event[k] = TRUE;
3006
3007           ei->event_page_nr[k] = j;
3008           ei->event_page[k] = &ei->change_page[j];
3009         }
3010       }
3011     }
3012   }
3013
3014   // ---------- initialize reference elements in change conditions ------------
3015
3016   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3017   {
3018     int element = EL_CUSTOM_START + i;
3019     struct ElementInfo *ei = &element_info[element];
3020
3021     for (j = 0; j < ei->num_change_pages; j++)
3022     {
3023       int trigger_element = ei->change_page[j].initial_trigger_element;
3024
3025       if (trigger_element >= EL_PREV_CE_8 &&
3026           trigger_element <= EL_NEXT_CE_8)
3027         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3028
3029       ei->change_page[j].trigger_element = trigger_element;
3030     }
3031   }
3032
3033   // ---------- initialize run-time trigger player and element ----------------
3034
3035   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3036   {
3037     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3038
3039     for (j = 0; j < ei->num_change_pages; j++)
3040     {
3041       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3042       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3043       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3044       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3045       ei->change_page[j].actual_trigger_ce_value = 0;
3046       ei->change_page[j].actual_trigger_ce_score = 0;
3047     }
3048   }
3049
3050   // ---------- initialize trigger events -------------------------------------
3051
3052   // initialize trigger events information
3053   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3054     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3055       trigger_events[i][j] = FALSE;
3056
3057   // add trigger events from element change event properties
3058   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3059   {
3060     struct ElementInfo *ei = &element_info[i];
3061
3062     for (j = 0; j < ei->num_change_pages; j++)
3063     {
3064       if (!ei->change_page[j].can_change_or_has_action)
3065         continue;
3066
3067       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3068       {
3069         int trigger_element = ei->change_page[j].trigger_element;
3070
3071         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3072         {
3073           if (ei->change_page[j].has_event[k])
3074           {
3075             if (IS_GROUP_ELEMENT(trigger_element))
3076             {
3077               struct ElementGroupInfo *group =
3078                 element_info[trigger_element].group;
3079
3080               for (l = 0; l < group->num_elements_resolved; l++)
3081                 trigger_events[group->element_resolved[l]][k] = TRUE;
3082             }
3083             else if (trigger_element == EL_ANY_ELEMENT)
3084               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3085                 trigger_events[l][k] = TRUE;
3086             else
3087               trigger_events[trigger_element][k] = TRUE;
3088           }
3089         }
3090       }
3091     }
3092   }
3093
3094   // ---------- initialize push delay -----------------------------------------
3095
3096   // initialize push delay values to default
3097   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3098   {
3099     if (!IS_CUSTOM_ELEMENT(i))
3100     {
3101       // set default push delay values (corrected since version 3.0.7-1)
3102       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3103       {
3104         element_info[i].push_delay_fixed = 2;
3105         element_info[i].push_delay_random = 8;
3106       }
3107       else
3108       {
3109         element_info[i].push_delay_fixed = 8;
3110         element_info[i].push_delay_random = 8;
3111       }
3112     }
3113   }
3114
3115   // set push delay value for certain elements from pre-defined list
3116   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3117   {
3118     int e = push_delay_list[i].element;
3119
3120     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3121     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3122   }
3123
3124   // set push delay value for Supaplex elements for newer engine versions
3125   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3126   {
3127     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3128     {
3129       if (IS_SP_ELEMENT(i))
3130       {
3131         // set SP push delay to just enough to push under a falling zonk
3132         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3133
3134         element_info[i].push_delay_fixed  = delay;
3135         element_info[i].push_delay_random = 0;
3136       }
3137     }
3138   }
3139
3140   // ---------- initialize move stepsize --------------------------------------
3141
3142   // initialize move stepsize values to default
3143   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3144     if (!IS_CUSTOM_ELEMENT(i))
3145       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3146
3147   // set move stepsize value for certain elements from pre-defined list
3148   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3149   {
3150     int e = move_stepsize_list[i].element;
3151
3152     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3153   }
3154
3155   // ---------- initialize collect score --------------------------------------
3156
3157   // initialize collect score values for custom elements from initial value
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_score = element_info[i].collect_score_initial;
3161
3162   // ---------- initialize collect count --------------------------------------
3163
3164   // initialize collect count values for non-custom elements
3165   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3166     if (!IS_CUSTOM_ELEMENT(i))
3167       element_info[i].collect_count_initial = 0;
3168
3169   // add collect count values for all elements from pre-defined list
3170   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3171     element_info[collect_count_list[i].element].collect_count_initial =
3172       collect_count_list[i].count;
3173
3174   // ---------- initialize access direction -----------------------------------
3175
3176   // initialize access direction values to default (access from every side)
3177   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3178     if (!IS_CUSTOM_ELEMENT(i))
3179       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3180
3181   // set access direction value for certain elements from pre-defined list
3182   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3183     element_info[access_direction_list[i].element].access_direction =
3184       access_direction_list[i].direction;
3185
3186   // ---------- initialize explosion content ----------------------------------
3187   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3188   {
3189     if (IS_CUSTOM_ELEMENT(i))
3190       continue;
3191
3192     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3193     {
3194       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3195
3196       element_info[i].content.e[x][y] =
3197         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3198          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3199          i == EL_PLAYER_3 ? EL_EMERALD :
3200          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3201          i == EL_MOLE ? EL_EMERALD_RED :
3202          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3203          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3204          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3205          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3206          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3207          i == EL_WALL_EMERALD ? EL_EMERALD :
3208          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3209          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3210          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3211          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3212          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3213          i == EL_WALL_PEARL ? EL_PEARL :
3214          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3215          EL_EMPTY);
3216     }
3217   }
3218
3219   // ---------- initialize recursion detection --------------------------------
3220   recursion_loop_depth = 0;
3221   recursion_loop_detected = FALSE;
3222   recursion_loop_element = EL_UNDEFINED;
3223
3224   // ---------- initialize graphics engine ------------------------------------
3225   game.scroll_delay_value =
3226     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3227      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3228   game.scroll_delay_value =
3229     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3230
3231   // ---------- initialize game engine snapshots ------------------------------
3232   for (i = 0; i < MAX_PLAYERS; i++)
3233     game.snapshot.last_action[i] = 0;
3234   game.snapshot.changed_action = FALSE;
3235   game.snapshot.collected_item = FALSE;
3236   game.snapshot.mode =
3237     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3238      SNAPSHOT_MODE_EVERY_STEP :
3239      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3240      SNAPSHOT_MODE_EVERY_MOVE :
3241      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3242      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3243   game.snapshot.save_snapshot = FALSE;
3244
3245   // ---------- initialize level time for Supaplex engine ---------------------
3246   // Supaplex levels with time limit currently unsupported -- should be added
3247   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3248     level.time = 0;
3249 }
3250
3251 static int get_num_special_action(int element, int action_first,
3252                                   int action_last)
3253 {
3254   int num_special_action = 0;
3255   int i, j;
3256
3257   for (i = action_first; i <= action_last; i++)
3258   {
3259     boolean found = FALSE;
3260
3261     for (j = 0; j < NUM_DIRECTIONS; j++)
3262       if (el_act_dir2img(element, i, j) !=
3263           el_act_dir2img(element, ACTION_DEFAULT, j))
3264         found = TRUE;
3265
3266     if (found)
3267       num_special_action++;
3268     else
3269       break;
3270   }
3271
3272   return num_special_action;
3273 }
3274
3275
3276 // ============================================================================
3277 // InitGame()
3278 // ----------------------------------------------------------------------------
3279 // initialize and start new game
3280 // ============================================================================
3281
3282 #if DEBUG_INIT_PLAYER
3283 static void DebugPrintPlayerStatus(char *message)
3284 {
3285   int i;
3286
3287   if (!options.debug)
3288     return;
3289
3290   printf("%s:\n", message);
3291
3292   for (i = 0; i < MAX_PLAYERS; i++)
3293   {
3294     struct PlayerInfo *player = &stored_player[i];
3295
3296     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3297            i + 1,
3298            player->present,
3299            player->connected,
3300            player->connected_locally,
3301            player->connected_network,
3302            player->active);
3303
3304     if (local_player == player)
3305       printf(" (local player)");
3306
3307     printf("\n");
3308   }
3309 }
3310 #endif
3311
3312 void InitGame(void)
3313 {
3314   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3315   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3316   int fade_mask = REDRAW_FIELD;
3317
3318   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3319   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3320   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3321   int initial_move_dir = MV_DOWN;
3322   int i, j, x, y;
3323
3324   // required here to update video display before fading (FIX THIS)
3325   DrawMaskedBorder(REDRAW_DOOR_2);
3326
3327   if (!game.restart_level)
3328     CloseDoor(DOOR_CLOSE_1);
3329
3330   SetGameStatus(GAME_MODE_PLAYING);
3331
3332   if (level_editor_test_game)
3333     FadeSkipNextFadeIn();
3334   else
3335     FadeSetEnterScreen();
3336
3337   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3338     fade_mask = REDRAW_ALL;
3339
3340   FadeLevelSoundsAndMusic();
3341
3342   ExpireSoundLoops(TRUE);
3343
3344   FadeOut(fade_mask);
3345
3346   // needed if different viewport properties defined for playing
3347   ChangeViewportPropertiesIfNeeded();
3348
3349   ClearField();
3350
3351   DrawCompleteVideoDisplay();
3352
3353   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3354
3355   InitGameEngine();
3356   InitGameControlValues();
3357
3358   // don't play tapes over network
3359   network_playing = (network.enabled && !tape.playing);
3360
3361   for (i = 0; i < MAX_PLAYERS; i++)
3362   {
3363     struct PlayerInfo *player = &stored_player[i];
3364
3365     player->index_nr = i;
3366     player->index_bit = (1 << i);
3367     player->element_nr = EL_PLAYER_1 + i;
3368
3369     player->present = FALSE;
3370     player->active = FALSE;
3371     player->mapped = FALSE;
3372
3373     player->killed = FALSE;
3374     player->reanimated = FALSE;
3375
3376     player->action = 0;
3377     player->effective_action = 0;
3378     player->programmed_action = 0;
3379
3380     player->mouse_action.lx = 0;
3381     player->mouse_action.ly = 0;
3382     player->mouse_action.button = 0;
3383     player->mouse_action.button_hint = 0;
3384
3385     player->effective_mouse_action.lx = 0;
3386     player->effective_mouse_action.ly = 0;
3387     player->effective_mouse_action.button = 0;
3388     player->effective_mouse_action.button_hint = 0;
3389
3390     player->score = 0;
3391     player->score_final = 0;
3392
3393     player->health = MAX_HEALTH;
3394     player->health_final = MAX_HEALTH;
3395
3396     player->gems_still_needed = level.gems_needed;
3397     player->sokoban_fields_still_needed = 0;
3398     player->sokoban_objects_still_needed = 0;
3399     player->lights_still_needed = 0;
3400     player->players_still_needed = 0;
3401     player->friends_still_needed = 0;
3402
3403     for (j = 0; j < MAX_NUM_KEYS; j++)
3404       player->key[j] = FALSE;
3405
3406     player->num_white_keys = 0;
3407
3408     player->dynabomb_count = 0;
3409     player->dynabomb_size = 1;
3410     player->dynabombs_left = 0;
3411     player->dynabomb_xl = FALSE;
3412
3413     player->MovDir = initial_move_dir;
3414     player->MovPos = 0;
3415     player->GfxPos = 0;
3416     player->GfxDir = initial_move_dir;
3417     player->GfxAction = ACTION_DEFAULT;
3418     player->Frame = 0;
3419     player->StepFrame = 0;
3420
3421     player->initial_element = player->element_nr;
3422     player->artwork_element =
3423       (level.use_artwork_element[i] ? level.artwork_element[i] :
3424        player->element_nr);
3425     player->use_murphy = FALSE;
3426
3427     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3428     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3429
3430     player->gravity = level.initial_player_gravity[i];
3431
3432     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3433
3434     player->actual_frame_counter = 0;
3435
3436     player->step_counter = 0;
3437
3438     player->last_move_dir = initial_move_dir;
3439
3440     player->is_active = FALSE;
3441
3442     player->is_waiting = FALSE;
3443     player->is_moving = FALSE;
3444     player->is_auto_moving = FALSE;
3445     player->is_digging = FALSE;
3446     player->is_snapping = FALSE;
3447     player->is_collecting = FALSE;
3448     player->is_pushing = FALSE;
3449     player->is_switching = FALSE;
3450     player->is_dropping = FALSE;
3451     player->is_dropping_pressed = FALSE;
3452
3453     player->is_bored = FALSE;
3454     player->is_sleeping = FALSE;
3455
3456     player->was_waiting = TRUE;
3457     player->was_moving = FALSE;
3458     player->was_snapping = FALSE;
3459     player->was_dropping = FALSE;
3460
3461     player->force_dropping = FALSE;
3462
3463     player->frame_counter_bored = -1;
3464     player->frame_counter_sleeping = -1;
3465
3466     player->anim_delay_counter = 0;
3467     player->post_delay_counter = 0;
3468
3469     player->dir_waiting = initial_move_dir;
3470     player->action_waiting = ACTION_DEFAULT;
3471     player->last_action_waiting = ACTION_DEFAULT;
3472     player->special_action_bored = ACTION_DEFAULT;
3473     player->special_action_sleeping = ACTION_DEFAULT;
3474
3475     player->switch_x = -1;
3476     player->switch_y = -1;
3477
3478     player->drop_x = -1;
3479     player->drop_y = -1;
3480
3481     player->show_envelope = 0;
3482
3483     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3484
3485     player->push_delay       = -1;      // initialized when pushing starts
3486     player->push_delay_value = game.initial_push_delay_value;
3487
3488     player->drop_delay = 0;
3489     player->drop_pressed_delay = 0;
3490
3491     player->last_jx = -1;
3492     player->last_jy = -1;
3493     player->jx = -1;
3494     player->jy = -1;
3495
3496     player->shield_normal_time_left = 0;
3497     player->shield_deadly_time_left = 0;
3498
3499     player->inventory_infinite_element = EL_UNDEFINED;
3500     player->inventory_size = 0;
3501
3502     if (level.use_initial_inventory[i])
3503     {
3504       for (j = 0; j < level.initial_inventory_size[i]; j++)
3505       {
3506         int element = level.initial_inventory_content[i][j];
3507         int collect_count = element_info[element].collect_count_initial;
3508         int k;
3509
3510         if (!IS_CUSTOM_ELEMENT(element))
3511           collect_count = 1;
3512
3513         if (collect_count == 0)
3514           player->inventory_infinite_element = element;
3515         else
3516           for (k = 0; k < collect_count; k++)
3517             if (player->inventory_size < MAX_INVENTORY_SIZE)
3518               player->inventory_element[player->inventory_size++] = element;
3519       }
3520     }
3521
3522     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3523     SnapField(player, 0, 0);
3524
3525     player->LevelSolved = FALSE;
3526     player->GameOver = FALSE;
3527
3528     player->LevelSolved_GameWon = FALSE;
3529     player->LevelSolved_GameEnd = FALSE;
3530     player->LevelSolved_SaveTape = FALSE;
3531     player->LevelSolved_SaveScore = FALSE;
3532
3533     player->LevelSolved_CountingTime = 0;
3534     player->LevelSolved_CountingScore = 0;
3535     player->LevelSolved_CountingHealth = 0;
3536
3537     map_player_action[i] = i;
3538   }
3539
3540   network_player_action_received = FALSE;
3541
3542   // initial null action
3543   if (network_playing)
3544     SendToServer_MovePlayer(MV_NONE);
3545
3546   ZX = ZY = -1;
3547   ExitX = ExitY = -1;
3548
3549   FrameCounter = 0;
3550   TimeFrames = 0;
3551   TimePlayed = 0;
3552   TimeLeft = level.time;
3553   TapeTime = 0;
3554
3555   ScreenMovDir = MV_NONE;
3556   ScreenMovPos = 0;
3557   ScreenGfxPos = 0;
3558
3559   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3560
3561   AllPlayersGone = FALSE;
3562
3563   game.panel.active = TRUE;
3564
3565   game.no_time_limit = (level.time == 0);
3566
3567   game.yamyam_content_nr = 0;
3568   game.robot_wheel_active = FALSE;
3569   game.magic_wall_active = FALSE;
3570   game.magic_wall_time_left = 0;
3571   game.light_time_left = 0;
3572   game.timegate_time_left = 0;
3573   game.switchgate_pos = 0;
3574   game.wind_direction = level.wind_direction_initial;
3575
3576   game.lenses_time_left = 0;
3577   game.magnify_time_left = 0;
3578
3579   game.ball_state = level.ball_state_initial;
3580   game.ball_content_nr = 0;
3581
3582   game.explosions_delayed = TRUE;
3583
3584   game.envelope_active = FALSE;
3585
3586   for (i = 0; i < NUM_BELTS; i++)
3587   {
3588     game.belt_dir[i] = MV_NONE;
3589     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3590   }
3591
3592   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3593     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3594
3595 #if DEBUG_INIT_PLAYER
3596   DebugPrintPlayerStatus("Player status at level initialization");
3597 #endif
3598
3599   SCAN_PLAYFIELD(x, y)
3600   {
3601     Feld[x][y] = Last[x][y] = level.field[x][y];
3602     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3603     ChangeDelay[x][y] = 0;
3604     ChangePage[x][y] = -1;
3605     CustomValue[x][y] = 0;              // initialized in InitField()
3606     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3607     AmoebaNr[x][y] = 0;
3608     WasJustMoving[x][y] = 0;
3609     WasJustFalling[x][y] = 0;
3610     CheckCollision[x][y] = 0;
3611     CheckImpact[x][y] = 0;
3612     Stop[x][y] = FALSE;
3613     Pushed[x][y] = FALSE;
3614
3615     ChangeCount[x][y] = 0;
3616     ChangeEvent[x][y] = -1;
3617
3618     ExplodePhase[x][y] = 0;
3619     ExplodeDelay[x][y] = 0;
3620     ExplodeField[x][y] = EX_TYPE_NONE;
3621
3622     RunnerVisit[x][y] = 0;
3623     PlayerVisit[x][y] = 0;
3624
3625     GfxFrame[x][y] = 0;
3626     GfxRandom[x][y] = INIT_GFX_RANDOM();
3627     GfxElement[x][y] = EL_UNDEFINED;
3628     GfxAction[x][y] = ACTION_DEFAULT;
3629     GfxDir[x][y] = MV_NONE;
3630     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3631   }
3632
3633   SCAN_PLAYFIELD(x, y)
3634   {
3635     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3636       emulate_bd = FALSE;
3637     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3638       emulate_sb = FALSE;
3639     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3640       emulate_sp = FALSE;
3641
3642     InitField(x, y, TRUE);
3643
3644     ResetGfxAnimation(x, y);
3645   }
3646
3647   InitBeltMovement();
3648
3649   for (i = 0; i < MAX_PLAYERS; i++)
3650   {
3651     struct PlayerInfo *player = &stored_player[i];
3652
3653     // set number of special actions for bored and sleeping animation
3654     player->num_special_action_bored =
3655       get_num_special_action(player->artwork_element,
3656                              ACTION_BORING_1, ACTION_BORING_LAST);
3657     player->num_special_action_sleeping =
3658       get_num_special_action(player->artwork_element,
3659                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3660   }
3661
3662   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3663                     emulate_sb ? EMU_SOKOBAN :
3664                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3665
3666   // initialize type of slippery elements
3667   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3668   {
3669     if (!IS_CUSTOM_ELEMENT(i))
3670     {
3671       // default: elements slip down either to the left or right randomly
3672       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3673
3674       // SP style elements prefer to slip down on the left side
3675       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3676         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3677
3678       // BD style elements prefer to slip down on the left side
3679       if (game.emulation == EMU_BOULDERDASH)
3680         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3681     }
3682   }
3683
3684   // initialize explosion and ignition delay
3685   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3686   {
3687     if (!IS_CUSTOM_ELEMENT(i))
3688     {
3689       int num_phase = 8;
3690       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3691                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3692                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3693       int last_phase = (num_phase + 1) * delay;
3694       int half_phase = (num_phase / 2) * delay;
3695
3696       element_info[i].explosion_delay = last_phase - 1;
3697       element_info[i].ignition_delay = half_phase;
3698
3699       if (i == EL_BLACK_ORB)
3700         element_info[i].ignition_delay = 1;
3701     }
3702   }
3703
3704   // correct non-moving belts to start moving left
3705   for (i = 0; i < NUM_BELTS; i++)
3706     if (game.belt_dir[i] == MV_NONE)
3707       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3708
3709 #if USE_NEW_PLAYER_ASSIGNMENTS
3710   for (i = 0; i < MAX_PLAYERS; i++)
3711   {
3712     stored_player[i].connected = FALSE;
3713
3714     // in network game mode, the local player might not be the first player
3715     if (stored_player[i].connected_locally)
3716       local_player = &stored_player[i];
3717   }
3718
3719   if (!network.enabled)
3720     local_player->connected = TRUE;
3721
3722   if (tape.playing)
3723   {
3724     for (i = 0; i < MAX_PLAYERS; i++)
3725       stored_player[i].connected = tape.player_participates[i];
3726   }
3727   else if (network.enabled)
3728   {
3729     // add team mode players connected over the network (needed for correct
3730     // assignment of player figures from level to locally playing players)
3731
3732     for (i = 0; i < MAX_PLAYERS; i++)
3733       if (stored_player[i].connected_network)
3734         stored_player[i].connected = TRUE;
3735   }
3736   else if (game.team_mode)
3737   {
3738     // try to guess locally connected team mode players (needed for correct
3739     // assignment of player figures from level to locally playing players)
3740
3741     for (i = 0; i < MAX_PLAYERS; i++)
3742       if (setup.input[i].use_joystick ||
3743           setup.input[i].key.left != KSYM_UNDEFINED)
3744         stored_player[i].connected = TRUE;
3745   }
3746
3747 #if DEBUG_INIT_PLAYER
3748   DebugPrintPlayerStatus("Player status after level initialization");
3749 #endif
3750
3751 #if DEBUG_INIT_PLAYER
3752   if (options.debug)
3753     printf("Reassigning players ...\n");
3754 #endif
3755
3756   // check if any connected player was not found in playfield
3757   for (i = 0; i < MAX_PLAYERS; i++)
3758   {
3759     struct PlayerInfo *player = &stored_player[i];
3760
3761     if (player->connected && !player->present)
3762     {
3763       struct PlayerInfo *field_player = NULL;
3764
3765 #if DEBUG_INIT_PLAYER
3766       if (options.debug)
3767         printf("- looking for field player for player %d ...\n", i + 1);
3768 #endif
3769
3770       // assign first free player found that is present in the playfield
3771
3772       // first try: look for unmapped playfield player that is not connected
3773       for (j = 0; j < MAX_PLAYERS; j++)
3774         if (field_player == NULL &&
3775             stored_player[j].present &&
3776             !stored_player[j].mapped &&
3777             !stored_player[j].connected)
3778           field_player = &stored_player[j];
3779
3780       // second try: look for *any* unmapped playfield player
3781       for (j = 0; j < MAX_PLAYERS; j++)
3782         if (field_player == NULL &&
3783             stored_player[j].present &&
3784             !stored_player[j].mapped)
3785           field_player = &stored_player[j];
3786
3787       if (field_player != NULL)
3788       {
3789         int jx = field_player->jx, jy = field_player->jy;
3790
3791 #if DEBUG_INIT_PLAYER
3792         if (options.debug)
3793           printf("- found player %d\n", field_player->index_nr + 1);
3794 #endif
3795
3796         player->present = FALSE;
3797         player->active = FALSE;
3798
3799         field_player->present = TRUE;
3800         field_player->active = TRUE;
3801
3802         /*
3803         player->initial_element = field_player->initial_element;
3804         player->artwork_element = field_player->artwork_element;
3805
3806         player->block_last_field       = field_player->block_last_field;
3807         player->block_delay_adjustment = field_player->block_delay_adjustment;
3808         */
3809
3810         StorePlayer[jx][jy] = field_player->element_nr;
3811
3812         field_player->jx = field_player->last_jx = jx;
3813         field_player->jy = field_player->last_jy = jy;
3814
3815         if (local_player == player)
3816           local_player = field_player;
3817
3818         map_player_action[field_player->index_nr] = i;
3819
3820         field_player->mapped = TRUE;
3821
3822 #if DEBUG_INIT_PLAYER
3823         if (options.debug)
3824           printf("- map_player_action[%d] == %d\n",
3825                  field_player->index_nr + 1, i + 1);
3826 #endif
3827       }
3828     }
3829
3830     if (player->connected && player->present)
3831       player->mapped = TRUE;
3832   }
3833
3834 #if DEBUG_INIT_PLAYER
3835   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3836 #endif
3837
3838 #else
3839
3840   // check if any connected player was not found in playfield
3841   for (i = 0; i < MAX_PLAYERS; i++)
3842   {
3843     struct PlayerInfo *player = &stored_player[i];
3844
3845     if (player->connected && !player->present)
3846     {
3847       for (j = 0; j < MAX_PLAYERS; j++)
3848       {
3849         struct PlayerInfo *field_player = &stored_player[j];
3850         int jx = field_player->jx, jy = field_player->jy;
3851
3852         // assign first free player found that is present in the playfield
3853         if (field_player->present && !field_player->connected)
3854         {
3855           player->present = TRUE;
3856           player->active = TRUE;
3857
3858           field_player->present = FALSE;
3859           field_player->active = FALSE;
3860
3861           player->initial_element = field_player->initial_element;
3862           player->artwork_element = field_player->artwork_element;
3863
3864           player->block_last_field       = field_player->block_last_field;
3865           player->block_delay_adjustment = field_player->block_delay_adjustment;
3866
3867           StorePlayer[jx][jy] = player->element_nr;
3868
3869           player->jx = player->last_jx = jx;
3870           player->jy = player->last_jy = jy;
3871
3872           break;
3873         }
3874       }
3875     }
3876   }
3877 #endif
3878
3879 #if 0
3880   printf("::: local_player->present == %d\n", local_player->present);
3881 #endif
3882
3883   // set focus to local player for network games, else to all players
3884   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3885   game.centered_player_nr_next = game.centered_player_nr;
3886   game.set_centered_player = FALSE;
3887
3888   if (network_playing && tape.recording)
3889   {
3890     // store client dependent player focus when recording network games
3891     tape.centered_player_nr_next = game.centered_player_nr_next;
3892     tape.set_centered_player = TRUE;
3893   }
3894
3895   if (tape.playing)
3896   {
3897     // when playing a tape, eliminate all players who do not participate
3898
3899 #if USE_NEW_PLAYER_ASSIGNMENTS
3900
3901     if (!game.team_mode)
3902     {
3903       for (i = 0; i < MAX_PLAYERS; i++)
3904       {
3905         if (stored_player[i].active &&
3906             !tape.player_participates[map_player_action[i]])
3907         {
3908           struct PlayerInfo *player = &stored_player[i];
3909           int jx = player->jx, jy = player->jy;
3910
3911 #if DEBUG_INIT_PLAYER
3912           if (options.debug)
3913             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3914 #endif
3915
3916           player->active = FALSE;
3917           StorePlayer[jx][jy] = 0;
3918           Feld[jx][jy] = EL_EMPTY;
3919         }
3920       }
3921     }
3922
3923 #else
3924
3925     for (i = 0; i < MAX_PLAYERS; i++)
3926     {
3927       if (stored_player[i].active &&
3928           !tape.player_participates[i])
3929       {
3930         struct PlayerInfo *player = &stored_player[i];
3931         int jx = player->jx, jy = player->jy;
3932
3933         player->active = FALSE;
3934         StorePlayer[jx][jy] = 0;
3935         Feld[jx][jy] = EL_EMPTY;
3936       }
3937     }
3938 #endif
3939   }
3940   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3941   {
3942     // when in single player mode, eliminate all but the local player
3943
3944     for (i = 0; i < MAX_PLAYERS; i++)
3945     {
3946       struct PlayerInfo *player = &stored_player[i];
3947
3948       if (player->active && player != local_player)
3949       {
3950         int jx = player->jx, jy = player->jy;
3951
3952         player->active = FALSE;
3953         player->present = FALSE;
3954
3955         StorePlayer[jx][jy] = 0;
3956         Feld[jx][jy] = EL_EMPTY;
3957       }
3958     }
3959   }
3960
3961   for (i = 0; i < MAX_PLAYERS; i++)
3962     if (stored_player[i].active)
3963       local_player->players_still_needed++;
3964
3965   if (level.solved_by_one_player)
3966     local_player->players_still_needed = 1;
3967
3968   // when recording the game, store which players take part in the game
3969   if (tape.recording)
3970   {
3971 #if USE_NEW_PLAYER_ASSIGNMENTS
3972     for (i = 0; i < MAX_PLAYERS; i++)
3973       if (stored_player[i].connected)
3974         tape.player_participates[i] = TRUE;
3975 #else
3976     for (i = 0; i < MAX_PLAYERS; i++)
3977       if (stored_player[i].active)
3978         tape.player_participates[i] = TRUE;
3979 #endif
3980   }
3981
3982 #if DEBUG_INIT_PLAYER
3983   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3984 #endif
3985
3986   if (BorderElement == EL_EMPTY)
3987   {
3988     SBX_Left = 0;
3989     SBX_Right = lev_fieldx - SCR_FIELDX;
3990     SBY_Upper = 0;
3991     SBY_Lower = lev_fieldy - SCR_FIELDY;
3992   }
3993   else
3994   {
3995     SBX_Left = -1;
3996     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3997     SBY_Upper = -1;
3998     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3999   }
4000
4001   if (full_lev_fieldx <= SCR_FIELDX)
4002     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4003   if (full_lev_fieldy <= SCR_FIELDY)
4004     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4005
4006   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4007     SBX_Left--;
4008   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4009     SBY_Upper--;
4010
4011   // if local player not found, look for custom element that might create
4012   // the player (make some assumptions about the right custom element)
4013   if (!local_player->present)
4014   {
4015     int start_x = 0, start_y = 0;
4016     int found_rating = 0;
4017     int found_element = EL_UNDEFINED;
4018     int player_nr = local_player->index_nr;
4019
4020     SCAN_PLAYFIELD(x, y)
4021     {
4022       int element = Feld[x][y];
4023       int content;
4024       int xx, yy;
4025       boolean is_player;
4026
4027       if (level.use_start_element[player_nr] &&
4028           level.start_element[player_nr] == element &&
4029           found_rating < 4)
4030       {
4031         start_x = x;
4032         start_y = y;
4033
4034         found_rating = 4;
4035         found_element = element;
4036       }
4037
4038       if (!IS_CUSTOM_ELEMENT(element))
4039         continue;
4040
4041       if (CAN_CHANGE(element))
4042       {
4043         for (i = 0; i < element_info[element].num_change_pages; i++)
4044         {
4045           // check for player created from custom element as single target
4046           content = element_info[element].change_page[i].target_element;
4047           is_player = ELEM_IS_PLAYER(content);
4048
4049           if (is_player && (found_rating < 3 ||
4050                             (found_rating == 3 && element < found_element)))
4051           {
4052             start_x = x;
4053             start_y = y;
4054
4055             found_rating = 3;
4056             found_element = element;
4057           }
4058         }
4059       }
4060
4061       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4062       {
4063         // check for player created from custom element as explosion content
4064         content = element_info[element].content.e[xx][yy];
4065         is_player = ELEM_IS_PLAYER(content);
4066
4067         if (is_player && (found_rating < 2 ||
4068                           (found_rating == 2 && element < found_element)))
4069         {
4070           start_x = x + xx - 1;
4071           start_y = y + yy - 1;
4072
4073           found_rating = 2;
4074           found_element = element;
4075         }
4076
4077         if (!CAN_CHANGE(element))
4078           continue;
4079
4080         for (i = 0; i < element_info[element].num_change_pages; i++)
4081         {
4082           // check for player created from custom element as extended target
4083           content =
4084             element_info[element].change_page[i].target_content.e[xx][yy];
4085
4086           is_player = ELEM_IS_PLAYER(content);
4087
4088           if (is_player && (found_rating < 1 ||
4089                             (found_rating == 1 && element < found_element)))
4090           {
4091             start_x = x + xx - 1;
4092             start_y = y + yy - 1;
4093
4094             found_rating = 1;
4095             found_element = element;
4096           }
4097         }
4098       }
4099     }
4100
4101     scroll_x = SCROLL_POSITION_X(start_x);
4102     scroll_y = SCROLL_POSITION_Y(start_y);
4103   }
4104   else
4105   {
4106     scroll_x = SCROLL_POSITION_X(local_player->jx);
4107     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4108   }
4109
4110   // !!! FIX THIS (START) !!!
4111   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4112   {
4113     InitGameEngine_EM();
4114   }
4115   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4116   {
4117     InitGameEngine_SP();
4118   }
4119   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4120   {
4121     InitGameEngine_MM();
4122   }
4123   else
4124   {
4125     DrawLevel(REDRAW_FIELD);
4126     DrawAllPlayers();
4127
4128     // after drawing the level, correct some elements
4129     if (game.timegate_time_left == 0)
4130       CloseAllOpenTimegates();
4131   }
4132
4133   // blit playfield from scroll buffer to normal back buffer for fading in
4134   BlitScreenToBitmap(backbuffer);
4135   // !!! FIX THIS (END) !!!
4136
4137   DrawMaskedBorder(fade_mask);
4138
4139   FadeIn(fade_mask);
4140
4141 #if 1
4142   // full screen redraw is required at this point in the following cases:
4143   // - special editor door undrawn when game was started from level editor
4144   // - drawing area (playfield) was changed and has to be removed completely
4145   redraw_mask = REDRAW_ALL;
4146   BackToFront();
4147 #endif
4148
4149   if (!game.restart_level)
4150   {
4151     // copy default game door content to main double buffer
4152
4153     // !!! CHECK AGAIN !!!
4154     SetPanelBackground();
4155     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4156     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4157   }
4158
4159   SetPanelBackground();
4160   SetDrawBackgroundMask(REDRAW_DOOR_1);
4161
4162   UpdateAndDisplayGameControlValues();
4163
4164   if (!game.restart_level)
4165   {
4166     UnmapGameButtons();
4167     UnmapTapeButtons();
4168
4169     FreeGameButtons();
4170     CreateGameButtons();
4171
4172     MapGameButtons();
4173     MapTapeButtons();
4174
4175     // copy actual game door content to door double buffer for OpenDoor()
4176     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4177
4178     OpenDoor(DOOR_OPEN_ALL);
4179
4180     KeyboardAutoRepeatOffUnlessAutoplay();
4181
4182 #if DEBUG_INIT_PLAYER
4183     DebugPrintPlayerStatus("Player status (final)");
4184 #endif
4185   }
4186
4187   UnmapAllGadgets();
4188
4189   MapGameButtons();
4190   MapTapeButtons();
4191
4192   if (!game.restart_level && !tape.playing)
4193   {
4194     LevelStats_incPlayed(level_nr);
4195
4196     SaveLevelSetup_SeriesInfo();
4197   }
4198
4199   game.restart_level = FALSE;
4200   game.restart_game_message = NULL;
4201   game.request_active = FALSE;
4202
4203   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4204     InitGameActions_MM();
4205
4206   SaveEngineSnapshotToListInitial();
4207
4208   if (!game.restart_level)
4209   {
4210     PlaySound(SND_GAME_STARTING);
4211
4212     if (setup.sound_music)
4213       PlayLevelMusic();
4214   }
4215 }
4216
4217 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4218                         int actual_player_x, int actual_player_y)
4219 {
4220   // this is used for non-R'n'D game engines to update certain engine values
4221
4222   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4223   {
4224     actual_player_x = correctLevelPosX_EM(actual_player_x);
4225     actual_player_y = correctLevelPosY_EM(actual_player_y);
4226   }
4227
4228   // needed to determine if sounds are played within the visible screen area
4229   scroll_x = actual_scroll_x;
4230   scroll_y = actual_scroll_y;
4231
4232   // needed to get player position for "follow finger" playing input method
4233   local_player->jx = actual_player_x;
4234   local_player->jy = actual_player_y;
4235 }
4236
4237 void InitMovDir(int x, int y)
4238 {
4239   int i, element = Feld[x][y];
4240   static int xy[4][2] =
4241   {
4242     {  0, +1 },
4243     { +1,  0 },
4244     {  0, -1 },
4245     { -1,  0 }
4246   };
4247   static int direction[3][4] =
4248   {
4249     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4250     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4251     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4252   };
4253
4254   switch (element)
4255   {
4256     case EL_BUG_RIGHT:
4257     case EL_BUG_UP:
4258     case EL_BUG_LEFT:
4259     case EL_BUG_DOWN:
4260       Feld[x][y] = EL_BUG;
4261       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4262       break;
4263
4264     case EL_SPACESHIP_RIGHT:
4265     case EL_SPACESHIP_UP:
4266     case EL_SPACESHIP_LEFT:
4267     case EL_SPACESHIP_DOWN:
4268       Feld[x][y] = EL_SPACESHIP;
4269       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4270       break;
4271
4272     case EL_BD_BUTTERFLY_RIGHT:
4273     case EL_BD_BUTTERFLY_UP:
4274     case EL_BD_BUTTERFLY_LEFT:
4275     case EL_BD_BUTTERFLY_DOWN:
4276       Feld[x][y] = EL_BD_BUTTERFLY;
4277       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4278       break;
4279
4280     case EL_BD_FIREFLY_RIGHT:
4281     case EL_BD_FIREFLY_UP:
4282     case EL_BD_FIREFLY_LEFT:
4283     case EL_BD_FIREFLY_DOWN:
4284       Feld[x][y] = EL_BD_FIREFLY;
4285       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4286       break;
4287
4288     case EL_PACMAN_RIGHT:
4289     case EL_PACMAN_UP:
4290     case EL_PACMAN_LEFT:
4291     case EL_PACMAN_DOWN:
4292       Feld[x][y] = EL_PACMAN;
4293       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4294       break;
4295
4296     case EL_YAMYAM_LEFT:
4297     case EL_YAMYAM_RIGHT:
4298     case EL_YAMYAM_UP:
4299     case EL_YAMYAM_DOWN:
4300       Feld[x][y] = EL_YAMYAM;
4301       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4302       break;
4303
4304     case EL_SP_SNIKSNAK:
4305       MovDir[x][y] = MV_UP;
4306       break;
4307
4308     case EL_SP_ELECTRON:
4309       MovDir[x][y] = MV_LEFT;
4310       break;
4311
4312     case EL_MOLE_LEFT:
4313     case EL_MOLE_RIGHT:
4314     case EL_MOLE_UP:
4315     case EL_MOLE_DOWN:
4316       Feld[x][y] = EL_MOLE;
4317       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4318       break;
4319
4320     default:
4321       if (IS_CUSTOM_ELEMENT(element))
4322       {
4323         struct ElementInfo *ei = &element_info[element];
4324         int move_direction_initial = ei->move_direction_initial;
4325         int move_pattern = ei->move_pattern;
4326
4327         if (move_direction_initial == MV_START_PREVIOUS)
4328         {
4329           if (MovDir[x][y] != MV_NONE)
4330             return;
4331
4332           move_direction_initial = MV_START_AUTOMATIC;
4333         }
4334
4335         if (move_direction_initial == MV_START_RANDOM)
4336           MovDir[x][y] = 1 << RND(4);
4337         else if (move_direction_initial & MV_ANY_DIRECTION)
4338           MovDir[x][y] = move_direction_initial;
4339         else if (move_pattern == MV_ALL_DIRECTIONS ||
4340                  move_pattern == MV_TURNING_LEFT ||
4341                  move_pattern == MV_TURNING_RIGHT ||
4342                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4343                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4344                  move_pattern == MV_TURNING_RANDOM)
4345           MovDir[x][y] = 1 << RND(4);
4346         else if (move_pattern == MV_HORIZONTAL)
4347           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4348         else if (move_pattern == MV_VERTICAL)
4349           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4350         else if (move_pattern & MV_ANY_DIRECTION)
4351           MovDir[x][y] = element_info[element].move_pattern;
4352         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4353                  move_pattern == MV_ALONG_RIGHT_SIDE)
4354         {
4355           // use random direction as default start direction
4356           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4357             MovDir[x][y] = 1 << RND(4);
4358
4359           for (i = 0; i < NUM_DIRECTIONS; i++)
4360           {
4361             int x1 = x + xy[i][0];
4362             int y1 = y + xy[i][1];
4363
4364             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4365             {
4366               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4367                 MovDir[x][y] = direction[0][i];
4368               else
4369                 MovDir[x][y] = direction[1][i];
4370
4371               break;
4372             }
4373           }
4374         }                
4375       }
4376       else
4377       {
4378         MovDir[x][y] = 1 << RND(4);
4379
4380         if (element != EL_BUG &&
4381             element != EL_SPACESHIP &&
4382             element != EL_BD_BUTTERFLY &&
4383             element != EL_BD_FIREFLY)
4384           break;
4385
4386         for (i = 0; i < NUM_DIRECTIONS; i++)
4387         {
4388           int x1 = x + xy[i][0];
4389           int y1 = y + xy[i][1];
4390
4391           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4392           {
4393             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4394             {
4395               MovDir[x][y] = direction[0][i];
4396               break;
4397             }
4398             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4399                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4400             {
4401               MovDir[x][y] = direction[1][i];
4402               break;
4403             }
4404           }
4405         }
4406       }
4407       break;
4408   }
4409
4410   GfxDir[x][y] = MovDir[x][y];
4411 }
4412
4413 void InitAmoebaNr(int x, int y)
4414 {
4415   int i;
4416   int group_nr = AmoebeNachbarNr(x, y);
4417
4418   if (group_nr == 0)
4419   {
4420     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4421     {
4422       if (AmoebaCnt[i] == 0)
4423       {
4424         group_nr = i;
4425         break;
4426       }
4427     }
4428   }
4429
4430   AmoebaNr[x][y] = group_nr;
4431   AmoebaCnt[group_nr]++;
4432   AmoebaCnt2[group_nr]++;
4433 }
4434
4435 static void PlayerWins(struct PlayerInfo *player)
4436 {
4437   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4438       local_player->players_still_needed > 0)
4439     return;
4440
4441   player->LevelSolved = TRUE;
4442   player->GameOver = TRUE;
4443
4444   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4445                          level.native_em_level->lev->score :
4446                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4447                          game_mm.score :
4448                          player->score);
4449   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4450                           MM_HEALTH(game_mm.laser_overload_value) :
4451                           player->health);
4452
4453   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4454                                       TimeLeft);
4455   player->LevelSolved_CountingScore = player->score_final;
4456   player->LevelSolved_CountingHealth = player->health_final;
4457 }
4458
4459 void GameWon(void)
4460 {
4461   static int time_count_steps;
4462   static int time, time_final;
4463   static int score, score_final;
4464   static int health, health_final;
4465   static int game_over_delay_1 = 0;
4466   static int game_over_delay_2 = 0;
4467   static int game_over_delay_3 = 0;
4468   int game_over_delay_value_1 = 50;
4469   int game_over_delay_value_2 = 25;
4470   int game_over_delay_value_3 = 50;
4471
4472   if (!local_player->LevelSolved_GameWon)
4473   {
4474     int i;
4475
4476     // do not start end game actions before the player stops moving (to exit)
4477     if (local_player->MovPos)
4478       return;
4479
4480     local_player->LevelSolved_GameWon = TRUE;
4481     local_player->LevelSolved_SaveTape = tape.recording;
4482     local_player->LevelSolved_SaveScore = !tape.playing;
4483
4484     if (!tape.playing)
4485     {
4486       LevelStats_incSolved(level_nr);
4487
4488       SaveLevelSetup_SeriesInfo();
4489     }
4490
4491     if (tape.auto_play)         // tape might already be stopped here
4492       tape.auto_play_level_solved = TRUE;
4493
4494     TapeStop();
4495
4496     game_over_delay_1 = 0;
4497     game_over_delay_2 = 0;
4498     game_over_delay_3 = game_over_delay_value_3;
4499
4500     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4501     score = score_final = local_player->score_final;
4502     health = health_final = local_player->health_final;
4503
4504     if (level.score[SC_TIME_BONUS] > 0)
4505     {
4506       if (TimeLeft > 0)
4507       {
4508         time_final = 0;
4509         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4510       }
4511       else if (game.no_time_limit && TimePlayed < 999)
4512       {
4513         time_final = 999;
4514         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4515       }
4516
4517       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4518
4519       game_over_delay_1 = game_over_delay_value_1;
4520
4521       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4522       {
4523         health_final = 0;
4524         score_final += health * level.score[SC_TIME_BONUS];
4525
4526         game_over_delay_2 = game_over_delay_value_2;
4527       }
4528
4529       local_player->score_final = score_final;
4530       local_player->health_final = health_final;
4531     }
4532
4533     if (level_editor_test_game)
4534     {
4535       time = time_final;
4536       score = score_final;
4537
4538       local_player->LevelSolved_CountingTime = time;
4539       local_player->LevelSolved_CountingScore = score;
4540
4541       game_panel_controls[GAME_PANEL_TIME].value = time;
4542       game_panel_controls[GAME_PANEL_SCORE].value = score;
4543
4544       DisplayGameControlValues();
4545     }
4546
4547     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4548     {
4549       if (ExitX >= 0 && ExitY >= 0)     // local player has left the level
4550       {
4551         // close exit door after last player
4552         if ((AllPlayersGone &&
4553              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4554               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4555               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4556             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4557             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4558         {
4559           int element = Feld[ExitX][ExitY];
4560
4561           Feld[ExitX][ExitY] =
4562             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4563              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4564              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4565              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4566              EL_EM_STEEL_EXIT_CLOSING);
4567
4568           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4569         }
4570
4571         // player disappears
4572         DrawLevelField(ExitX, ExitY);
4573       }
4574
4575       for (i = 0; i < MAX_PLAYERS; i++)
4576       {
4577         struct PlayerInfo *player = &stored_player[i];
4578
4579         if (player->present)
4580         {
4581           RemovePlayer(player);
4582
4583           // player disappears
4584           DrawLevelField(player->jx, player->jy);
4585         }
4586       }
4587     }
4588
4589     PlaySound(SND_GAME_WINNING);
4590   }
4591
4592   if (game_over_delay_1 > 0)
4593   {
4594     game_over_delay_1--;
4595
4596     return;
4597   }
4598
4599   if (time != time_final)
4600   {
4601     int time_to_go = ABS(time_final - time);
4602     int time_count_dir = (time < time_final ? +1 : -1);
4603
4604     if (time_to_go < time_count_steps)
4605       time_count_steps = 1;
4606
4607     time  += time_count_steps * time_count_dir;
4608     score += time_count_steps * level.score[SC_TIME_BONUS];
4609
4610     local_player->LevelSolved_CountingTime = time;
4611     local_player->LevelSolved_CountingScore = score;
4612
4613     game_panel_controls[GAME_PANEL_TIME].value = time;
4614     game_panel_controls[GAME_PANEL_SCORE].value = score;
4615
4616     DisplayGameControlValues();
4617
4618     if (time == time_final)
4619       StopSound(SND_GAME_LEVELTIME_BONUS);
4620     else if (setup.sound_loops)
4621       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4622     else
4623       PlaySound(SND_GAME_LEVELTIME_BONUS);
4624
4625     return;
4626   }
4627
4628   if (game_over_delay_2 > 0)
4629   {
4630     game_over_delay_2--;
4631
4632     return;
4633   }
4634
4635   if (health != health_final)
4636   {
4637     int health_count_dir = (health < health_final ? +1 : -1);
4638
4639     health += health_count_dir;
4640     score  += level.score[SC_TIME_BONUS];
4641
4642     local_player->LevelSolved_CountingHealth = health;
4643     local_player->LevelSolved_CountingScore = score;
4644
4645     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4646     game_panel_controls[GAME_PANEL_SCORE].value = score;
4647
4648     DisplayGameControlValues();
4649
4650     if (health == health_final)
4651       StopSound(SND_GAME_LEVELTIME_BONUS);
4652     else if (setup.sound_loops)
4653       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4654     else
4655       PlaySound(SND_GAME_LEVELTIME_BONUS);
4656
4657     return;
4658   }
4659
4660   game.panel.active = FALSE;
4661
4662   if (game_over_delay_3 > 0)
4663   {
4664     game_over_delay_3--;
4665
4666     return;
4667   }
4668
4669   GameEnd();
4670 }
4671
4672 void GameEnd(void)
4673 {
4674   // used instead of "level_nr" (needed for network games)
4675   int last_level_nr = levelset.level_nr;
4676   int hi_pos;
4677
4678   local_player->LevelSolved_GameEnd = TRUE;
4679
4680   if (local_player->LevelSolved_SaveTape)
4681   {
4682     // make sure that request dialog to save tape does not open door again
4683     if (!global.use_envelope_request)
4684       CloseDoor(DOOR_CLOSE_1);
4685
4686     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4687   }
4688
4689   // if no tape is to be saved, close both doors simultaneously
4690   CloseDoor(DOOR_CLOSE_ALL);
4691
4692   if (level_editor_test_game)
4693   {
4694     SetGameStatus(GAME_MODE_MAIN);
4695
4696     DrawMainMenu();
4697
4698     return;
4699   }
4700
4701   if (!local_player->LevelSolved_SaveScore)
4702   {
4703     SetGameStatus(GAME_MODE_MAIN);
4704
4705     DrawMainMenu();
4706
4707     return;
4708   }
4709
4710   if (level_nr == leveldir_current->handicap_level)
4711   {
4712     leveldir_current->handicap_level++;
4713
4714     SaveLevelSetup_SeriesInfo();
4715   }
4716
4717   if (setup.increment_levels &&
4718       level_nr < leveldir_current->last_level &&
4719       !network_playing)
4720   {
4721     level_nr++;         // advance to next level
4722     TapeErase();        // start with empty tape
4723
4724     if (setup.auto_play_next_level)
4725     {
4726       LoadLevel(level_nr);
4727
4728       SaveLevelSetup_SeriesInfo();
4729     }
4730   }
4731
4732   hi_pos = NewHiScore(last_level_nr);
4733
4734   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4735   {
4736     SetGameStatus(GAME_MODE_SCORES);
4737
4738     DrawHallOfFame(last_level_nr, hi_pos);
4739   }
4740   else if (setup.auto_play_next_level && setup.increment_levels &&
4741            last_level_nr < leveldir_current->last_level &&
4742            !network_playing)
4743   {
4744     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4745   }
4746   else
4747   {
4748     SetGameStatus(GAME_MODE_MAIN);
4749
4750     DrawMainMenu();
4751   }
4752 }
4753
4754 int NewHiScore(int level_nr)
4755 {
4756   int k, l;
4757   int position = -1;
4758   boolean one_score_entry_per_name = !program.many_scores_per_name;
4759
4760   LoadScore(level_nr);
4761
4762   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4763       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4764     return -1;
4765
4766   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4767   {
4768     if (local_player->score_final > highscore[k].Score)
4769     {
4770       // player has made it to the hall of fame
4771
4772       if (k < MAX_SCORE_ENTRIES - 1)
4773       {
4774         int m = MAX_SCORE_ENTRIES - 1;
4775
4776         if (one_score_entry_per_name)
4777         {
4778           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4779             if (strEqual(setup.player_name, highscore[l].Name))
4780               m = l;
4781
4782           if (m == k)   // player's new highscore overwrites his old one
4783             goto put_into_list;
4784         }
4785
4786         for (l = m; l > k; l--)
4787         {
4788           strcpy(highscore[l].Name, highscore[l - 1].Name);
4789           highscore[l].Score = highscore[l - 1].Score;
4790         }
4791       }
4792
4793       put_into_list:
4794
4795       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4796       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4797       highscore[k].Score = local_player->score_final; 
4798       position = k;
4799
4800       break;
4801     }
4802     else if (one_score_entry_per_name &&
4803              !strncmp(setup.player_name, highscore[k].Name,
4804                       MAX_PLAYER_NAME_LEN))
4805       break;    // player already there with a higher score
4806   }
4807
4808   if (position >= 0) 
4809     SaveScore(level_nr);
4810
4811   return position;
4812 }
4813
4814 static int getElementMoveStepsizeExt(int x, int y, int direction)
4815 {
4816   int element = Feld[x][y];
4817   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4818   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4819   int horiz_move = (dx != 0);
4820   int sign = (horiz_move ? dx : dy);
4821   int step = sign * element_info[element].move_stepsize;
4822
4823   // special values for move stepsize for spring and things on conveyor belt
4824   if (horiz_move)
4825   {
4826     if (CAN_FALL(element) &&
4827         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4828       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4829     else if (element == EL_SPRING)
4830       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4831   }
4832
4833   return step;
4834 }
4835
4836 static int getElementMoveStepsize(int x, int y)
4837 {
4838   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4839 }
4840
4841 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4842 {
4843   if (player->GfxAction != action || player->GfxDir != dir)
4844   {
4845     player->GfxAction = action;
4846     player->GfxDir = dir;
4847     player->Frame = 0;
4848     player->StepFrame = 0;
4849   }
4850 }
4851
4852 static void ResetGfxFrame(int x, int y)
4853 {
4854   // profiling showed that "autotest" spends 10~20% of its time in this function
4855   if (DrawingDeactivatedField())
4856     return;
4857
4858   int element = Feld[x][y];
4859   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4860
4861   if (graphic_info[graphic].anim_global_sync)
4862     GfxFrame[x][y] = FrameCounter;
4863   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4864     GfxFrame[x][y] = CustomValue[x][y];
4865   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4866     GfxFrame[x][y] = element_info[element].collect_score;
4867   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4868     GfxFrame[x][y] = ChangeDelay[x][y];
4869 }
4870
4871 static void ResetGfxAnimation(int x, int y)
4872 {
4873   GfxAction[x][y] = ACTION_DEFAULT;
4874   GfxDir[x][y] = MovDir[x][y];
4875   GfxFrame[x][y] = 0;
4876
4877   ResetGfxFrame(x, y);
4878 }
4879
4880 static void ResetRandomAnimationValue(int x, int y)
4881 {
4882   GfxRandom[x][y] = INIT_GFX_RANDOM();
4883 }
4884
4885 static void InitMovingField(int x, int y, int direction)
4886 {
4887   int element = Feld[x][y];
4888   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4889   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4890   int newx = x + dx;
4891   int newy = y + dy;
4892   boolean is_moving_before, is_moving_after;
4893
4894   // check if element was/is moving or being moved before/after mode change
4895   is_moving_before = (WasJustMoving[x][y] != 0);
4896   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4897
4898   // reset animation only for moving elements which change direction of moving
4899   // or which just started or stopped moving
4900   // (else CEs with property "can move" / "not moving" are reset each frame)
4901   if (is_moving_before != is_moving_after ||
4902       direction != MovDir[x][y])
4903     ResetGfxAnimation(x, y);
4904
4905   MovDir[x][y] = direction;
4906   GfxDir[x][y] = direction;
4907
4908   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4909                      direction == MV_DOWN && CAN_FALL(element) ?
4910                      ACTION_FALLING : ACTION_MOVING);
4911
4912   // this is needed for CEs with property "can move" / "not moving"
4913
4914   if (is_moving_after)
4915   {
4916     if (Feld[newx][newy] == EL_EMPTY)
4917       Feld[newx][newy] = EL_BLOCKED;
4918
4919     MovDir[newx][newy] = MovDir[x][y];
4920
4921     CustomValue[newx][newy] = CustomValue[x][y];
4922
4923     GfxFrame[newx][newy] = GfxFrame[x][y];
4924     GfxRandom[newx][newy] = GfxRandom[x][y];
4925     GfxAction[newx][newy] = GfxAction[x][y];
4926     GfxDir[newx][newy] = GfxDir[x][y];
4927   }
4928 }
4929
4930 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4931 {
4932   int direction = MovDir[x][y];
4933   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4934   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4935
4936   *goes_to_x = newx;
4937   *goes_to_y = newy;
4938 }
4939
4940 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4941 {
4942   int oldx = x, oldy = y;
4943   int direction = MovDir[x][y];
4944
4945   if (direction == MV_LEFT)
4946     oldx++;
4947   else if (direction == MV_RIGHT)
4948     oldx--;
4949   else if (direction == MV_UP)
4950     oldy++;
4951   else if (direction == MV_DOWN)
4952     oldy--;
4953
4954   *comes_from_x = oldx;
4955   *comes_from_y = oldy;
4956 }
4957
4958 static int MovingOrBlocked2Element(int x, int y)
4959 {
4960   int element = Feld[x][y];
4961
4962   if (element == EL_BLOCKED)
4963   {
4964     int oldx, oldy;
4965
4966     Blocked2Moving(x, y, &oldx, &oldy);
4967     return Feld[oldx][oldy];
4968   }
4969   else
4970     return element;
4971 }
4972
4973 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4974 {
4975   // like MovingOrBlocked2Element(), but if element is moving
4976   // and (x,y) is the field the moving element is just leaving,
4977   // return EL_BLOCKED instead of the element value
4978   int element = Feld[x][y];
4979
4980   if (IS_MOVING(x, y))
4981   {
4982     if (element == EL_BLOCKED)
4983     {
4984       int oldx, oldy;
4985
4986       Blocked2Moving(x, y, &oldx, &oldy);
4987       return Feld[oldx][oldy];
4988     }
4989     else
4990       return EL_BLOCKED;
4991   }
4992   else
4993     return element;
4994 }
4995
4996 static void RemoveField(int x, int y)
4997 {
4998   Feld[x][y] = EL_EMPTY;
4999
5000   MovPos[x][y] = 0;
5001   MovDir[x][y] = 0;
5002   MovDelay[x][y] = 0;
5003
5004   CustomValue[x][y] = 0;
5005
5006   AmoebaNr[x][y] = 0;
5007   ChangeDelay[x][y] = 0;
5008   ChangePage[x][y] = -1;
5009   Pushed[x][y] = FALSE;
5010
5011   GfxElement[x][y] = EL_UNDEFINED;
5012   GfxAction[x][y] = ACTION_DEFAULT;
5013   GfxDir[x][y] = MV_NONE;
5014 }
5015
5016 static void RemoveMovingField(int x, int y)
5017 {
5018   int oldx = x, oldy = y, newx = x, newy = y;
5019   int element = Feld[x][y];
5020   int next_element = EL_UNDEFINED;
5021
5022   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5023     return;
5024
5025   if (IS_MOVING(x, y))
5026   {
5027     Moving2Blocked(x, y, &newx, &newy);
5028
5029     if (Feld[newx][newy] != EL_BLOCKED)
5030     {
5031       // element is moving, but target field is not free (blocked), but
5032       // already occupied by something different (example: acid pool);
5033       // in this case, only remove the moving field, but not the target
5034
5035       RemoveField(oldx, oldy);
5036
5037       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5038
5039       TEST_DrawLevelField(oldx, oldy);
5040
5041       return;
5042     }
5043   }
5044   else if (element == EL_BLOCKED)
5045   {
5046     Blocked2Moving(x, y, &oldx, &oldy);
5047     if (!IS_MOVING(oldx, oldy))
5048       return;
5049   }
5050
5051   if (element == EL_BLOCKED &&
5052       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5053        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5054        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5055        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5056        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5057        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5058     next_element = get_next_element(Feld[oldx][oldy]);
5059
5060   RemoveField(oldx, oldy);
5061   RemoveField(newx, newy);
5062
5063   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5064
5065   if (next_element != EL_UNDEFINED)
5066     Feld[oldx][oldy] = next_element;
5067
5068   TEST_DrawLevelField(oldx, oldy);
5069   TEST_DrawLevelField(newx, newy);
5070 }
5071
5072 void DrawDynamite(int x, int y)
5073 {
5074   int sx = SCREENX(x), sy = SCREENY(y);
5075   int graphic = el2img(Feld[x][y]);
5076   int frame;
5077
5078   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5079     return;
5080
5081   if (IS_WALKABLE_INSIDE(Back[x][y]))
5082     return;
5083
5084   if (Back[x][y])
5085     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5086   else if (Store[x][y])
5087     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5088
5089   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5090
5091   if (Back[x][y] || Store[x][y])
5092     DrawGraphicThruMask(sx, sy, graphic, frame);
5093   else
5094     DrawGraphic(sx, sy, graphic, frame);
5095 }
5096
5097 static void CheckDynamite(int x, int y)
5098 {
5099   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5100   {
5101     MovDelay[x][y]--;
5102
5103     if (MovDelay[x][y] != 0)
5104     {
5105       DrawDynamite(x, y);
5106       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5107
5108       return;
5109     }
5110   }
5111
5112   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5113
5114   Bang(x, y);
5115 }
5116
5117 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5118 {
5119   boolean num_checked_players = 0;
5120   int i;
5121
5122   for (i = 0; i < MAX_PLAYERS; i++)
5123   {
5124     if (stored_player[i].active)
5125     {
5126       int sx = stored_player[i].jx;
5127       int sy = stored_player[i].jy;
5128
5129       if (num_checked_players == 0)
5130       {
5131         *sx1 = *sx2 = sx;
5132         *sy1 = *sy2 = sy;
5133       }
5134       else
5135       {
5136         *sx1 = MIN(*sx1, sx);
5137         *sy1 = MIN(*sy1, sy);
5138         *sx2 = MAX(*sx2, sx);
5139         *sy2 = MAX(*sy2, sy);
5140       }
5141
5142       num_checked_players++;
5143     }
5144   }
5145 }
5146
5147 static boolean checkIfAllPlayersFitToScreen_RND(void)
5148 {
5149   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5150
5151   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5152
5153   return (sx2 - sx1 < SCR_FIELDX &&
5154           sy2 - sy1 < SCR_FIELDY);
5155 }
5156
5157 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5158 {
5159   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5160
5161   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5162
5163   *sx = (sx1 + sx2) / 2;
5164   *sy = (sy1 + sy2) / 2;
5165 }
5166
5167 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5168                                boolean center_screen, boolean quick_relocation)
5169 {
5170   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5171   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5172   boolean no_delay = (tape.warp_forward);
5173   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5174   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5175   int new_scroll_x, new_scroll_y;
5176
5177   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5178   {
5179     // case 1: quick relocation inside visible screen (without scrolling)
5180
5181     RedrawPlayfield();
5182
5183     return;
5184   }
5185
5186   if (!level.shifted_relocation || center_screen)
5187   {
5188     // relocation _with_ centering of screen
5189
5190     new_scroll_x = SCROLL_POSITION_X(x);
5191     new_scroll_y = SCROLL_POSITION_Y(y);
5192   }
5193   else
5194   {
5195     // relocation _without_ centering of screen
5196
5197     int center_scroll_x = SCROLL_POSITION_X(old_x);
5198     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5199     int offset_x = x + (scroll_x - center_scroll_x);
5200     int offset_y = y + (scroll_y - center_scroll_y);
5201
5202     // for new screen position, apply previous offset to center position
5203     new_scroll_x = SCROLL_POSITION_X(offset_x);
5204     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5205   }
5206
5207   if (quick_relocation)
5208   {
5209     // case 2: quick relocation (redraw without visible scrolling)
5210
5211     scroll_x = new_scroll_x;
5212     scroll_y = new_scroll_y;
5213
5214     RedrawPlayfield();
5215
5216     return;
5217   }
5218
5219   // case 3: visible relocation (with scrolling to new position)
5220
5221   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5222
5223   SetVideoFrameDelay(wait_delay_value);
5224
5225   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5226   {
5227     int dx = 0, dy = 0;
5228     int fx = FX, fy = FY;
5229
5230     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5231     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5232
5233     if (dx == 0 && dy == 0)             // no scrolling needed at all
5234       break;
5235
5236     scroll_x -= dx;
5237     scroll_y -= dy;
5238
5239     fx += dx * TILEX / 2;
5240     fy += dy * TILEY / 2;
5241
5242     ScrollLevel(dx, dy);
5243     DrawAllPlayers();
5244
5245     // scroll in two steps of half tile size to make things smoother
5246     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5247
5248     // scroll second step to align at full tile size
5249     BlitScreenToBitmap(window);
5250   }
5251
5252   DrawAllPlayers();
5253   BackToFront();
5254
5255   SetVideoFrameDelay(frame_delay_value_old);
5256 }
5257
5258 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5259 {
5260   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5261   int player_nr = GET_PLAYER_NR(el_player);
5262   struct PlayerInfo *player = &stored_player[player_nr];
5263   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5264   boolean no_delay = (tape.warp_forward);
5265   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5266   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5267   int old_jx = player->jx;
5268   int old_jy = player->jy;
5269   int old_element = Feld[old_jx][old_jy];
5270   int element = Feld[jx][jy];
5271   boolean player_relocated = (old_jx != jx || old_jy != jy);
5272
5273   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5274   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5275   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5276   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5277   int leave_side_horiz = move_dir_horiz;
5278   int leave_side_vert  = move_dir_vert;
5279   int enter_side = enter_side_horiz | enter_side_vert;
5280   int leave_side = leave_side_horiz | leave_side_vert;
5281
5282   if (player->GameOver)         // do not reanimate dead player
5283     return;
5284
5285   if (!player_relocated)        // no need to relocate the player
5286     return;
5287
5288   if (IS_PLAYER(jx, jy))        // player already placed at new position
5289   {
5290     RemoveField(jx, jy);        // temporarily remove newly placed player
5291     DrawLevelField(jx, jy);
5292   }
5293
5294   if (player->present)
5295   {
5296     while (player->MovPos)
5297     {
5298       ScrollPlayer(player, SCROLL_GO_ON);
5299       ScrollScreen(NULL, SCROLL_GO_ON);
5300
5301       AdvanceFrameAndPlayerCounters(player->index_nr);
5302
5303       DrawPlayer(player);
5304
5305       BackToFront_WithFrameDelay(wait_delay_value);
5306     }
5307
5308     DrawPlayer(player);         // needed here only to cleanup last field
5309     DrawLevelField(player->jx, player->jy);     // remove player graphic
5310
5311     player->is_moving = FALSE;
5312   }
5313
5314   if (IS_CUSTOM_ELEMENT(old_element))
5315     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5316                                CE_LEFT_BY_PLAYER,
5317                                player->index_bit, leave_side);
5318
5319   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5320                                       CE_PLAYER_LEAVES_X,
5321                                       player->index_bit, leave_side);
5322
5323   Feld[jx][jy] = el_player;
5324   InitPlayerField(jx, jy, el_player, TRUE);
5325
5326   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5327      possible that the relocation target field did not contain a player element,
5328      but a walkable element, to which the new player was relocated -- in this
5329      case, restore that (already initialized!) element on the player field */
5330   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5331   {
5332     Feld[jx][jy] = element;     // restore previously existing element
5333   }
5334
5335   // only visually relocate centered player
5336   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5337                      FALSE, level.instant_relocation);
5338
5339   TestIfPlayerTouchesBadThing(jx, jy);
5340   TestIfPlayerTouchesCustomElement(jx, jy);
5341
5342   if (IS_CUSTOM_ELEMENT(element))
5343     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5344                                player->index_bit, enter_side);
5345
5346   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5347                                       player->index_bit, enter_side);
5348
5349   if (player->is_switching)
5350   {
5351     /* ensure that relocation while still switching an element does not cause
5352        a new element to be treated as also switched directly after relocation
5353        (this is important for teleporter switches that teleport the player to
5354        a place where another teleporter switch is in the same direction, which
5355        would then incorrectly be treated as immediately switched before the
5356        direction key that caused the switch was released) */
5357
5358     player->switch_x += jx - old_jx;
5359     player->switch_y += jy - old_jy;
5360   }
5361 }
5362
5363 static void Explode(int ex, int ey, int phase, int mode)
5364 {
5365   int x, y;
5366   int last_phase;
5367   int border_element;
5368
5369   // !!! eliminate this variable !!!
5370   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5371
5372   if (game.explosions_delayed)
5373   {
5374     ExplodeField[ex][ey] = mode;
5375     return;
5376   }
5377
5378   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5379   {
5380     int center_element = Feld[ex][ey];
5381     int artwork_element, explosion_element;     // set these values later
5382
5383     // remove things displayed in background while burning dynamite
5384     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5385       Back[ex][ey] = 0;
5386
5387     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5388     {
5389       // put moving element to center field (and let it explode there)
5390       center_element = MovingOrBlocked2Element(ex, ey);
5391       RemoveMovingField(ex, ey);
5392       Feld[ex][ey] = center_element;
5393     }
5394
5395     // now "center_element" is finally determined -- set related values now
5396     artwork_element = center_element;           // for custom player artwork
5397     explosion_element = center_element;         // for custom player artwork
5398
5399     if (IS_PLAYER(ex, ey))
5400     {
5401       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5402
5403       artwork_element = stored_player[player_nr].artwork_element;
5404
5405       if (level.use_explosion_element[player_nr])
5406       {
5407         explosion_element = level.explosion_element[player_nr];
5408         artwork_element = explosion_element;
5409       }
5410     }
5411
5412     if (mode == EX_TYPE_NORMAL ||
5413         mode == EX_TYPE_CENTER ||
5414         mode == EX_TYPE_CROSS)
5415       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5416
5417     last_phase = element_info[explosion_element].explosion_delay + 1;
5418
5419     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5420     {
5421       int xx = x - ex + 1;
5422       int yy = y - ey + 1;
5423       int element;
5424
5425       if (!IN_LEV_FIELD(x, y) ||
5426           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5427           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5428         continue;
5429
5430       element = Feld[x][y];
5431
5432       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5433       {
5434         element = MovingOrBlocked2Element(x, y);
5435
5436         if (!IS_EXPLOSION_PROOF(element))
5437           RemoveMovingField(x, y);
5438       }
5439
5440       // indestructible elements can only explode in center (but not flames)
5441       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5442                                            mode == EX_TYPE_BORDER)) ||
5443           element == EL_FLAMES)
5444         continue;
5445
5446       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5447          behaviour, for example when touching a yamyam that explodes to rocks
5448          with active deadly shield, a rock is created under the player !!! */
5449       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5450 #if 0
5451       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5452           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5453            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5454 #else
5455       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5456 #endif
5457       {
5458         if (IS_ACTIVE_BOMB(element))
5459         {
5460           // re-activate things under the bomb like gate or penguin
5461           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5462           Back[x][y] = 0;
5463         }
5464
5465         continue;
5466       }
5467
5468       // save walkable background elements while explosion on same tile
5469       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5470           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5471         Back[x][y] = element;
5472
5473       // ignite explodable elements reached by other explosion
5474       if (element == EL_EXPLOSION)
5475         element = Store2[x][y];
5476
5477       if (AmoebaNr[x][y] &&
5478           (element == EL_AMOEBA_FULL ||
5479            element == EL_BD_AMOEBA ||
5480            element == EL_AMOEBA_GROWING))
5481       {
5482         AmoebaCnt[AmoebaNr[x][y]]--;
5483         AmoebaCnt2[AmoebaNr[x][y]]--;
5484       }
5485
5486       RemoveField(x, y);
5487
5488       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5489       {
5490         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5491
5492         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5493
5494         if (PLAYERINFO(ex, ey)->use_murphy)
5495           Store[x][y] = EL_EMPTY;
5496       }
5497
5498       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5499       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5500       else if (ELEM_IS_PLAYER(center_element))
5501         Store[x][y] = EL_EMPTY;
5502       else if (center_element == EL_YAMYAM)
5503         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5504       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5505         Store[x][y] = element_info[center_element].content.e[xx][yy];
5506 #if 1
5507       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5508       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5509       // otherwise) -- FIX THIS !!!
5510       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5511         Store[x][y] = element_info[element].content.e[1][1];
5512 #else
5513       else if (!CAN_EXPLODE(element))
5514         Store[x][y] = element_info[element].content.e[1][1];
5515 #endif
5516       else
5517         Store[x][y] = EL_EMPTY;
5518
5519       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5520           center_element == EL_AMOEBA_TO_DIAMOND)
5521         Store2[x][y] = element;
5522
5523       Feld[x][y] = EL_EXPLOSION;
5524       GfxElement[x][y] = artwork_element;
5525
5526       ExplodePhase[x][y] = 1;
5527       ExplodeDelay[x][y] = last_phase;
5528
5529       Stop[x][y] = TRUE;
5530     }
5531
5532     if (center_element == EL_YAMYAM)
5533       game.yamyam_content_nr =
5534         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5535
5536     return;
5537   }
5538
5539   if (Stop[ex][ey])
5540     return;
5541
5542   x = ex;
5543   y = ey;
5544
5545   if (phase == 1)
5546     GfxFrame[x][y] = 0;         // restart explosion animation
5547
5548   last_phase = ExplodeDelay[x][y];
5549
5550   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5551
5552   // this can happen if the player leaves an explosion just in time
5553   if (GfxElement[x][y] == EL_UNDEFINED)
5554     GfxElement[x][y] = EL_EMPTY;
5555
5556   border_element = Store2[x][y];
5557   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5558     border_element = StorePlayer[x][y];
5559
5560   if (phase == element_info[border_element].ignition_delay ||
5561       phase == last_phase)
5562   {
5563     boolean border_explosion = FALSE;
5564
5565     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5566         !PLAYER_EXPLOSION_PROTECTED(x, y))
5567     {
5568       KillPlayerUnlessExplosionProtected(x, y);
5569       border_explosion = TRUE;
5570     }
5571     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5572     {
5573       Feld[x][y] = Store2[x][y];
5574       Store2[x][y] = 0;
5575       Bang(x, y);
5576       border_explosion = TRUE;
5577     }
5578     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5579     {
5580       AmoebeUmwandeln(x, y);
5581       Store2[x][y] = 0;
5582       border_explosion = TRUE;
5583     }
5584
5585     // if an element just explodes due to another explosion (chain-reaction),
5586     // do not immediately end the new explosion when it was the last frame of
5587     // the explosion (as it would be done in the following "if"-statement!)
5588     if (border_explosion && phase == last_phase)
5589       return;
5590   }
5591
5592   if (phase == last_phase)
5593   {
5594     int element;
5595
5596     element = Feld[x][y] = Store[x][y];
5597     Store[x][y] = Store2[x][y] = 0;
5598     GfxElement[x][y] = EL_UNDEFINED;
5599
5600     // player can escape from explosions and might therefore be still alive
5601     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5602         element <= EL_PLAYER_IS_EXPLODING_4)
5603     {
5604       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5605       int explosion_element = EL_PLAYER_1 + player_nr;
5606       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5607       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5608
5609       if (level.use_explosion_element[player_nr])
5610         explosion_element = level.explosion_element[player_nr];
5611
5612       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5613                     element_info[explosion_element].content.e[xx][yy]);
5614     }
5615
5616     // restore probably existing indestructible background element
5617     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5618       element = Feld[x][y] = Back[x][y];
5619     Back[x][y] = 0;
5620
5621     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5622     GfxDir[x][y] = MV_NONE;
5623     ChangeDelay[x][y] = 0;
5624     ChangePage[x][y] = -1;
5625
5626     CustomValue[x][y] = 0;
5627
5628     InitField_WithBug2(x, y, FALSE);
5629
5630     TEST_DrawLevelField(x, y);
5631
5632     TestIfElementTouchesCustomElement(x, y);
5633
5634     if (GFX_CRUMBLED(element))
5635       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5636
5637     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5638       StorePlayer[x][y] = 0;
5639
5640     if (ELEM_IS_PLAYER(element))
5641       RelocatePlayer(x, y, element);
5642   }
5643   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5644   {
5645     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5646     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5647
5648     if (phase == delay)
5649       TEST_DrawLevelFieldCrumbled(x, y);
5650
5651     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5652     {
5653       DrawLevelElement(x, y, Back[x][y]);
5654       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5655     }
5656     else if (IS_WALKABLE_UNDER(Back[x][y]))
5657     {
5658       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5659       DrawLevelElementThruMask(x, y, Back[x][y]);
5660     }
5661     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5662       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5663   }
5664 }
5665
5666 static void DynaExplode(int ex, int ey)
5667 {
5668   int i, j;
5669   int dynabomb_element = Feld[ex][ey];
5670   int dynabomb_size = 1;
5671   boolean dynabomb_xl = FALSE;
5672   struct PlayerInfo *player;
5673   static int xy[4][2] =
5674   {
5675     { 0, -1 },
5676     { -1, 0 },
5677     { +1, 0 },
5678     { 0, +1 }
5679   };
5680
5681   if (IS_ACTIVE_BOMB(dynabomb_element))
5682   {
5683     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5684     dynabomb_size = player->dynabomb_size;
5685     dynabomb_xl = player->dynabomb_xl;
5686     player->dynabombs_left++;
5687   }
5688
5689   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5690
5691   for (i = 0; i < NUM_DIRECTIONS; i++)
5692   {
5693     for (j = 1; j <= dynabomb_size; j++)
5694     {
5695       int x = ex + j * xy[i][0];
5696       int y = ey + j * xy[i][1];
5697       int element;
5698
5699       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5700         break;
5701
5702       element = Feld[x][y];
5703
5704       // do not restart explosions of fields with active bombs
5705       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5706         continue;
5707
5708       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5709
5710       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5711           !IS_DIGGABLE(element) && !dynabomb_xl)
5712         break;
5713     }
5714   }
5715 }
5716
5717 void Bang(int x, int y)
5718 {
5719   int element = MovingOrBlocked2Element(x, y);
5720   int explosion_type = EX_TYPE_NORMAL;
5721
5722   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5723   {
5724     struct PlayerInfo *player = PLAYERINFO(x, y);
5725
5726     element = Feld[x][y] = player->initial_element;
5727
5728     if (level.use_explosion_element[player->index_nr])
5729     {
5730       int explosion_element = level.explosion_element[player->index_nr];
5731
5732       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5733         explosion_type = EX_TYPE_CROSS;
5734       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5735         explosion_type = EX_TYPE_CENTER;
5736     }
5737   }
5738
5739   switch (element)
5740   {
5741     case EL_BUG:
5742     case EL_SPACESHIP:
5743     case EL_BD_BUTTERFLY:
5744     case EL_BD_FIREFLY:
5745     case EL_YAMYAM:
5746     case EL_DARK_YAMYAM:
5747     case EL_ROBOT:
5748     case EL_PACMAN:
5749     case EL_MOLE:
5750       RaiseScoreElement(element);
5751       break;
5752
5753     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5754     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5755     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5756     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5757     case EL_DYNABOMB_INCREASE_NUMBER:
5758     case EL_DYNABOMB_INCREASE_SIZE:
5759     case EL_DYNABOMB_INCREASE_POWER:
5760       explosion_type = EX_TYPE_DYNA;
5761       break;
5762
5763     case EL_DC_LANDMINE:
5764       explosion_type = EX_TYPE_CENTER;
5765       break;
5766
5767     case EL_PENGUIN:
5768     case EL_LAMP:
5769     case EL_LAMP_ACTIVE:
5770     case EL_AMOEBA_TO_DIAMOND:
5771       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5772         explosion_type = EX_TYPE_CENTER;
5773       break;
5774
5775     default:
5776       if (element_info[element].explosion_type == EXPLODES_CROSS)
5777         explosion_type = EX_TYPE_CROSS;
5778       else if (element_info[element].explosion_type == EXPLODES_1X1)
5779         explosion_type = EX_TYPE_CENTER;
5780       break;
5781   }
5782
5783   if (explosion_type == EX_TYPE_DYNA)
5784     DynaExplode(x, y);
5785   else
5786     Explode(x, y, EX_PHASE_START, explosion_type);
5787
5788   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5789 }
5790
5791 static void SplashAcid(int x, int y)
5792 {
5793   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5794       (!IN_LEV_FIELD(x - 1, y - 2) ||
5795        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5796     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5797
5798   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5799       (!IN_LEV_FIELD(x + 1, y - 2) ||
5800        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5801     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5802
5803   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5804 }
5805
5806 static void InitBeltMovement(void)
5807 {
5808   static int belt_base_element[4] =
5809   {
5810     EL_CONVEYOR_BELT_1_LEFT,
5811     EL_CONVEYOR_BELT_2_LEFT,
5812     EL_CONVEYOR_BELT_3_LEFT,
5813     EL_CONVEYOR_BELT_4_LEFT
5814   };
5815   static int belt_base_active_element[4] =
5816   {
5817     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5818     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5819     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5820     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5821   };
5822
5823   int x, y, i, j;
5824
5825   // set frame order for belt animation graphic according to belt direction
5826   for (i = 0; i < NUM_BELTS; i++)
5827   {
5828     int belt_nr = i;
5829
5830     for (j = 0; j < NUM_BELT_PARTS; j++)
5831     {
5832       int element = belt_base_active_element[belt_nr] + j;
5833       int graphic_1 = el2img(element);
5834       int graphic_2 = el2panelimg(element);
5835
5836       if (game.belt_dir[i] == MV_LEFT)
5837       {
5838         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5839         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5840       }
5841       else
5842       {
5843         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5844         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5845       }
5846     }
5847   }
5848
5849   SCAN_PLAYFIELD(x, y)
5850   {
5851     int element = Feld[x][y];
5852
5853     for (i = 0; i < NUM_BELTS; i++)
5854     {
5855       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5856       {
5857         int e_belt_nr = getBeltNrFromBeltElement(element);
5858         int belt_nr = i;
5859
5860         if (e_belt_nr == belt_nr)
5861         {
5862           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5863
5864           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5865         }
5866       }
5867     }
5868   }
5869 }
5870
5871 static void ToggleBeltSwitch(int x, int y)
5872 {
5873   static int belt_base_element[4] =
5874   {
5875     EL_CONVEYOR_BELT_1_LEFT,
5876     EL_CONVEYOR_BELT_2_LEFT,
5877     EL_CONVEYOR_BELT_3_LEFT,
5878     EL_CONVEYOR_BELT_4_LEFT
5879   };
5880   static int belt_base_active_element[4] =
5881   {
5882     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5883     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5884     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5885     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5886   };
5887   static int belt_base_switch_element[4] =
5888   {
5889     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5890     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5891     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5892     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5893   };
5894   static int belt_move_dir[4] =
5895   {
5896     MV_LEFT,
5897     MV_NONE,
5898     MV_RIGHT,
5899     MV_NONE,
5900   };
5901
5902   int element = Feld[x][y];
5903   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5904   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5905   int belt_dir = belt_move_dir[belt_dir_nr];
5906   int xx, yy, i;
5907
5908   if (!IS_BELT_SWITCH(element))
5909     return;
5910
5911   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5912   game.belt_dir[belt_nr] = belt_dir;
5913
5914   if (belt_dir_nr == 3)
5915     belt_dir_nr = 1;
5916
5917   // set frame order for belt animation graphic according to belt direction
5918   for (i = 0; i < NUM_BELT_PARTS; i++)
5919   {
5920     int element = belt_base_active_element[belt_nr] + i;
5921     int graphic_1 = el2img(element);
5922     int graphic_2 = el2panelimg(element);
5923
5924     if (belt_dir == MV_LEFT)
5925     {
5926       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5927       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5928     }
5929     else
5930     {
5931       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5932       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5933     }
5934   }
5935
5936   SCAN_PLAYFIELD(xx, yy)
5937   {
5938     int element = Feld[xx][yy];
5939
5940     if (IS_BELT_SWITCH(element))
5941     {
5942       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5943
5944       if (e_belt_nr == belt_nr)
5945       {
5946         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5947         TEST_DrawLevelField(xx, yy);
5948       }
5949     }
5950     else if (IS_BELT(element) && belt_dir != MV_NONE)
5951     {
5952       int e_belt_nr = getBeltNrFromBeltElement(element);
5953
5954       if (e_belt_nr == belt_nr)
5955       {
5956         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5957
5958         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5959         TEST_DrawLevelField(xx, yy);
5960       }
5961     }
5962     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5963     {
5964       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5965
5966       if (e_belt_nr == belt_nr)
5967       {
5968         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5969
5970         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5971         TEST_DrawLevelField(xx, yy);
5972       }
5973     }
5974   }
5975 }
5976
5977 static void ToggleSwitchgateSwitch(int x, int y)
5978 {
5979   int xx, yy;
5980
5981   game.switchgate_pos = !game.switchgate_pos;
5982
5983   SCAN_PLAYFIELD(xx, yy)
5984   {
5985     int element = Feld[xx][yy];
5986
5987     if (element == EL_SWITCHGATE_SWITCH_UP)
5988     {
5989       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5990       TEST_DrawLevelField(xx, yy);
5991     }
5992     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5993     {
5994       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5995       TEST_DrawLevelField(xx, yy);
5996     }
5997     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5998     {
5999       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6000       TEST_DrawLevelField(xx, yy);
6001     }
6002     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6003     {
6004       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6005       TEST_DrawLevelField(xx, yy);
6006     }
6007     else if (element == EL_SWITCHGATE_OPEN ||
6008              element == EL_SWITCHGATE_OPENING)
6009     {
6010       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6011
6012       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6013     }
6014     else if (element == EL_SWITCHGATE_CLOSED ||
6015              element == EL_SWITCHGATE_CLOSING)
6016     {
6017       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6018
6019       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6020     }
6021   }
6022 }
6023
6024 static int getInvisibleActiveFromInvisibleElement(int element)
6025 {
6026   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6027           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6028           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6029           element);
6030 }
6031
6032 static int getInvisibleFromInvisibleActiveElement(int element)
6033 {
6034   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6035           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6036           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6037           element);
6038 }
6039
6040 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6041 {
6042   int x, y;
6043
6044   SCAN_PLAYFIELD(x, y)
6045   {
6046     int element = Feld[x][y];
6047
6048     if (element == EL_LIGHT_SWITCH &&
6049         game.light_time_left > 0)
6050     {
6051       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6052       TEST_DrawLevelField(x, y);
6053     }
6054     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6055              game.light_time_left == 0)
6056     {
6057       Feld[x][y] = EL_LIGHT_SWITCH;
6058       TEST_DrawLevelField(x, y);
6059     }
6060     else if (element == EL_EMC_DRIPPER &&
6061              game.light_time_left > 0)
6062     {
6063       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6064       TEST_DrawLevelField(x, y);
6065     }
6066     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6067              game.light_time_left == 0)
6068     {
6069       Feld[x][y] = EL_EMC_DRIPPER;
6070       TEST_DrawLevelField(x, y);
6071     }
6072     else if (element == EL_INVISIBLE_STEELWALL ||
6073              element == EL_INVISIBLE_WALL ||
6074              element == EL_INVISIBLE_SAND)
6075     {
6076       if (game.light_time_left > 0)
6077         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6078
6079       TEST_DrawLevelField(x, y);
6080
6081       // uncrumble neighbour fields, if needed
6082       if (element == EL_INVISIBLE_SAND)
6083         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6084     }
6085     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6086              element == EL_INVISIBLE_WALL_ACTIVE ||
6087              element == EL_INVISIBLE_SAND_ACTIVE)
6088     {
6089       if (game.light_time_left == 0)
6090         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6091
6092       TEST_DrawLevelField(x, y);
6093
6094       // re-crumble neighbour fields, if needed
6095       if (element == EL_INVISIBLE_SAND)
6096         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6097     }
6098   }
6099 }
6100
6101 static void RedrawAllInvisibleElementsForLenses(void)
6102 {
6103   int x, y;
6104
6105   SCAN_PLAYFIELD(x, y)
6106   {
6107     int element = Feld[x][y];
6108
6109     if (element == EL_EMC_DRIPPER &&
6110         game.lenses_time_left > 0)
6111     {
6112       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6113       TEST_DrawLevelField(x, y);
6114     }
6115     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6116              game.lenses_time_left == 0)
6117     {
6118       Feld[x][y] = EL_EMC_DRIPPER;
6119       TEST_DrawLevelField(x, y);
6120     }
6121     else if (element == EL_INVISIBLE_STEELWALL ||
6122              element == EL_INVISIBLE_WALL ||
6123              element == EL_INVISIBLE_SAND)
6124     {
6125       if (game.lenses_time_left > 0)
6126         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6127
6128       TEST_DrawLevelField(x, y);
6129
6130       // uncrumble neighbour fields, if needed
6131       if (element == EL_INVISIBLE_SAND)
6132         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6133     }
6134     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6135              element == EL_INVISIBLE_WALL_ACTIVE ||
6136              element == EL_INVISIBLE_SAND_ACTIVE)
6137     {
6138       if (game.lenses_time_left == 0)
6139         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6140
6141       TEST_DrawLevelField(x, y);
6142
6143       // re-crumble neighbour fields, if needed
6144       if (element == EL_INVISIBLE_SAND)
6145         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6146     }
6147   }
6148 }
6149
6150 static void RedrawAllInvisibleElementsForMagnifier(void)
6151 {
6152   int x, y;
6153
6154   SCAN_PLAYFIELD(x, y)
6155   {
6156     int element = Feld[x][y];
6157
6158     if (element == EL_EMC_FAKE_GRASS &&
6159         game.magnify_time_left > 0)
6160     {
6161       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6162       TEST_DrawLevelField(x, y);
6163     }
6164     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6165              game.magnify_time_left == 0)
6166     {
6167       Feld[x][y] = EL_EMC_FAKE_GRASS;
6168       TEST_DrawLevelField(x, y);
6169     }
6170     else if (IS_GATE_GRAY(element) &&
6171              game.magnify_time_left > 0)
6172     {
6173       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6174                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6175                     IS_EM_GATE_GRAY(element) ?
6176                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6177                     IS_EMC_GATE_GRAY(element) ?
6178                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6179                     IS_DC_GATE_GRAY(element) ?
6180                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6181                     element);
6182       TEST_DrawLevelField(x, y);
6183     }
6184     else if (IS_GATE_GRAY_ACTIVE(element) &&
6185              game.magnify_time_left == 0)
6186     {
6187       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6188                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6189                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6190                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6191                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6192                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6193                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6194                     EL_DC_GATE_WHITE_GRAY :
6195                     element);
6196       TEST_DrawLevelField(x, y);
6197     }
6198   }
6199 }
6200
6201 static void ToggleLightSwitch(int x, int y)
6202 {
6203   int element = Feld[x][y];
6204
6205   game.light_time_left =
6206     (element == EL_LIGHT_SWITCH ?
6207      level.time_light * FRAMES_PER_SECOND : 0);
6208
6209   RedrawAllLightSwitchesAndInvisibleElements();
6210 }
6211
6212 static void ActivateTimegateSwitch(int x, int y)
6213 {
6214   int xx, yy;
6215
6216   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6217
6218   SCAN_PLAYFIELD(xx, yy)
6219   {
6220     int element = Feld[xx][yy];
6221
6222     if (element == EL_TIMEGATE_CLOSED ||
6223         element == EL_TIMEGATE_CLOSING)
6224     {
6225       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6226       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6227     }
6228
6229     /*
6230     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6231     {
6232       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6233       TEST_DrawLevelField(xx, yy);
6234     }
6235     */
6236
6237   }
6238
6239   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6240                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6241 }
6242
6243 static void Impact(int x, int y)
6244 {
6245   boolean last_line = (y == lev_fieldy - 1);
6246   boolean object_hit = FALSE;
6247   boolean impact = (last_line || object_hit);
6248   int element = Feld[x][y];
6249   int smashed = EL_STEELWALL;
6250
6251   if (!last_line)       // check if element below was hit
6252   {
6253     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6254       return;
6255
6256     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6257                                          MovDir[x][y + 1] != MV_DOWN ||
6258                                          MovPos[x][y + 1] <= TILEY / 2));
6259
6260     // do not smash moving elements that left the smashed field in time
6261     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6262         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6263       object_hit = FALSE;
6264
6265 #if USE_QUICKSAND_IMPACT_BUGFIX
6266     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6267     {
6268       RemoveMovingField(x, y + 1);
6269       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6270       Feld[x][y + 2] = EL_ROCK;
6271       TEST_DrawLevelField(x, y + 2);
6272
6273       object_hit = TRUE;
6274     }
6275
6276     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6277     {
6278       RemoveMovingField(x, y + 1);
6279       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6280       Feld[x][y + 2] = EL_ROCK;
6281       TEST_DrawLevelField(x, y + 2);
6282
6283       object_hit = TRUE;
6284     }
6285 #endif
6286
6287     if (object_hit)
6288       smashed = MovingOrBlocked2Element(x, y + 1);
6289
6290     impact = (last_line || object_hit);
6291   }
6292
6293   if (!last_line && smashed == EL_ACID) // element falls into acid
6294   {
6295     SplashAcid(x, y + 1);
6296     return;
6297   }
6298
6299   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6300   // only reset graphic animation if graphic really changes after impact
6301   if (impact &&
6302       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6303   {
6304     ResetGfxAnimation(x, y);
6305     TEST_DrawLevelField(x, y);
6306   }
6307
6308   if (impact && CAN_EXPLODE_IMPACT(element))
6309   {
6310     Bang(x, y);
6311     return;
6312   }
6313   else if (impact && element == EL_PEARL &&
6314            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6315   {
6316     ResetGfxAnimation(x, y);
6317
6318     Feld[x][y] = EL_PEARL_BREAKING;
6319     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6320     return;
6321   }
6322   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6323   {
6324     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6325
6326     return;
6327   }
6328
6329   if (impact && element == EL_AMOEBA_DROP)
6330   {
6331     if (object_hit && IS_PLAYER(x, y + 1))
6332       KillPlayerUnlessEnemyProtected(x, y + 1);
6333     else if (object_hit && smashed == EL_PENGUIN)
6334       Bang(x, y + 1);
6335     else
6336     {
6337       Feld[x][y] = EL_AMOEBA_GROWING;
6338       Store[x][y] = EL_AMOEBA_WET;
6339
6340       ResetRandomAnimationValue(x, y);
6341     }
6342     return;
6343   }
6344
6345   if (object_hit)               // check which object was hit
6346   {
6347     if ((CAN_PASS_MAGIC_WALL(element) && 
6348          (smashed == EL_MAGIC_WALL ||
6349           smashed == EL_BD_MAGIC_WALL)) ||
6350         (CAN_PASS_DC_MAGIC_WALL(element) &&
6351          smashed == EL_DC_MAGIC_WALL))
6352     {
6353       int xx, yy;
6354       int activated_magic_wall =
6355         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6356          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6357          EL_DC_MAGIC_WALL_ACTIVE);
6358
6359       // activate magic wall / mill
6360       SCAN_PLAYFIELD(xx, yy)
6361       {
6362         if (Feld[xx][yy] == smashed)
6363           Feld[xx][yy] = activated_magic_wall;
6364       }
6365
6366       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6367       game.magic_wall_active = TRUE;
6368
6369       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6370                             SND_MAGIC_WALL_ACTIVATING :
6371                             smashed == EL_BD_MAGIC_WALL ?
6372                             SND_BD_MAGIC_WALL_ACTIVATING :
6373                             SND_DC_MAGIC_WALL_ACTIVATING));
6374     }
6375
6376     if (IS_PLAYER(x, y + 1))
6377     {
6378       if (CAN_SMASH_PLAYER(element))
6379       {
6380         KillPlayerUnlessEnemyProtected(x, y + 1);
6381         return;
6382       }
6383     }
6384     else if (smashed == EL_PENGUIN)
6385     {
6386       if (CAN_SMASH_PLAYER(element))
6387       {
6388         Bang(x, y + 1);
6389         return;
6390       }
6391     }
6392     else if (element == EL_BD_DIAMOND)
6393     {
6394       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6395       {
6396         Bang(x, y + 1);
6397         return;
6398       }
6399     }
6400     else if (((element == EL_SP_INFOTRON ||
6401                element == EL_SP_ZONK) &&
6402               (smashed == EL_SP_SNIKSNAK ||
6403                smashed == EL_SP_ELECTRON ||
6404                smashed == EL_SP_DISK_ORANGE)) ||
6405              (element == EL_SP_INFOTRON &&
6406               smashed == EL_SP_DISK_YELLOW))
6407     {
6408       Bang(x, y + 1);
6409       return;
6410     }
6411     else if (CAN_SMASH_EVERYTHING(element))
6412     {
6413       if (IS_CLASSIC_ENEMY(smashed) ||
6414           CAN_EXPLODE_SMASHED(smashed))
6415       {
6416         Bang(x, y + 1);
6417         return;
6418       }
6419       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6420       {
6421         if (smashed == EL_LAMP ||
6422             smashed == EL_LAMP_ACTIVE)
6423         {
6424           Bang(x, y + 1);
6425           return;
6426         }
6427         else if (smashed == EL_NUT)
6428         {
6429           Feld[x][y + 1] = EL_NUT_BREAKING;
6430           PlayLevelSound(x, y, SND_NUT_BREAKING);
6431           RaiseScoreElement(EL_NUT);
6432           return;
6433         }
6434         else if (smashed == EL_PEARL)
6435         {
6436           ResetGfxAnimation(x, y);
6437
6438           Feld[x][y + 1] = EL_PEARL_BREAKING;
6439           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6440           return;
6441         }
6442         else if (smashed == EL_DIAMOND)
6443         {
6444           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6445           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6446           return;
6447         }
6448         else if (IS_BELT_SWITCH(smashed))
6449         {
6450           ToggleBeltSwitch(x, y + 1);
6451         }
6452         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6453                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6454                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6455                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6456         {
6457           ToggleSwitchgateSwitch(x, y + 1);
6458         }
6459         else if (smashed == EL_LIGHT_SWITCH ||
6460                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6461         {
6462           ToggleLightSwitch(x, y + 1);
6463         }
6464         else
6465         {
6466           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6467
6468           CheckElementChangeBySide(x, y + 1, smashed, element,
6469                                    CE_SWITCHED, CH_SIDE_TOP);
6470           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6471                                             CH_SIDE_TOP);
6472         }
6473       }
6474       else
6475       {
6476         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6477       }
6478     }
6479   }
6480
6481   // play sound of magic wall / mill
6482   if (!last_line &&
6483       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6484        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6485        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6486   {
6487     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6488       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6489     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6490       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6491     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6492       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6493
6494     return;
6495   }
6496
6497   // play sound of object that hits the ground
6498   if (last_line || object_hit)
6499     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6500 }
6501
6502 static void TurnRoundExt(int x, int y)
6503 {
6504   static struct
6505   {
6506     int dx, dy;
6507   } move_xy[] =
6508   {
6509     {  0,  0 },
6510     { -1,  0 },
6511     { +1,  0 },
6512     {  0,  0 },
6513     {  0, -1 },
6514     {  0,  0 }, { 0, 0 }, { 0, 0 },
6515     {  0, +1 }
6516   };
6517   static struct
6518   {
6519     int left, right, back;
6520   } turn[] =
6521   {
6522     { 0,        0,              0        },
6523     { MV_DOWN,  MV_UP,          MV_RIGHT },
6524     { MV_UP,    MV_DOWN,        MV_LEFT  },
6525     { 0,        0,              0        },
6526     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6527     { 0,        0,              0        },
6528     { 0,        0,              0        },
6529     { 0,        0,              0        },
6530     { MV_RIGHT, MV_LEFT,        MV_UP    }
6531   };
6532
6533   int element = Feld[x][y];
6534   int move_pattern = element_info[element].move_pattern;
6535
6536   int old_move_dir = MovDir[x][y];
6537   int left_dir  = turn[old_move_dir].left;
6538   int right_dir = turn[old_move_dir].right;
6539   int back_dir  = turn[old_move_dir].back;
6540
6541   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6542   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6543   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6544   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6545
6546   int left_x  = x + left_dx,  left_y  = y + left_dy;
6547   int right_x = x + right_dx, right_y = y + right_dy;
6548   int move_x  = x + move_dx,  move_y  = y + move_dy;
6549
6550   int xx, yy;
6551
6552   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6553   {
6554     TestIfBadThingTouchesOtherBadThing(x, y);
6555
6556     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6557       MovDir[x][y] = right_dir;
6558     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6559       MovDir[x][y] = left_dir;
6560
6561     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6562       MovDelay[x][y] = 9;
6563     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6564       MovDelay[x][y] = 1;
6565   }
6566   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6567   {
6568     TestIfBadThingTouchesOtherBadThing(x, y);
6569
6570     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6571       MovDir[x][y] = left_dir;
6572     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6573       MovDir[x][y] = right_dir;
6574
6575     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6576       MovDelay[x][y] = 9;
6577     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6578       MovDelay[x][y] = 1;
6579   }
6580   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6581   {
6582     TestIfBadThingTouchesOtherBadThing(x, y);
6583
6584     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6585       MovDir[x][y] = left_dir;
6586     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6587       MovDir[x][y] = right_dir;
6588
6589     if (MovDir[x][y] != old_move_dir)
6590       MovDelay[x][y] = 9;
6591   }
6592   else if (element == EL_YAMYAM)
6593   {
6594     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6595     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6596
6597     if (can_turn_left && can_turn_right)
6598       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6599     else if (can_turn_left)
6600       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6601     else if (can_turn_right)
6602       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6603     else
6604       MovDir[x][y] = back_dir;
6605
6606     MovDelay[x][y] = 16 + 16 * RND(3);
6607   }
6608   else if (element == EL_DARK_YAMYAM)
6609   {
6610     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6611                                                          left_x, left_y);
6612     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6613                                                          right_x, right_y);
6614
6615     if (can_turn_left && can_turn_right)
6616       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6617     else if (can_turn_left)
6618       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6619     else if (can_turn_right)
6620       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6621     else
6622       MovDir[x][y] = back_dir;
6623
6624     MovDelay[x][y] = 16 + 16 * RND(3);
6625   }
6626   else if (element == EL_PACMAN)
6627   {
6628     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6629     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6630
6631     if (can_turn_left && can_turn_right)
6632       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6633     else if (can_turn_left)
6634       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6635     else if (can_turn_right)
6636       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6637     else
6638       MovDir[x][y] = back_dir;
6639
6640     MovDelay[x][y] = 6 + RND(40);
6641   }
6642   else if (element == EL_PIG)
6643   {
6644     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6645     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6646     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6647     boolean should_turn_left, should_turn_right, should_move_on;
6648     int rnd_value = 24;
6649     int rnd = RND(rnd_value);
6650
6651     should_turn_left = (can_turn_left &&
6652                         (!can_move_on ||
6653                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6654                                                    y + back_dy + left_dy)));
6655     should_turn_right = (can_turn_right &&
6656                          (!can_move_on ||
6657                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6658                                                     y + back_dy + right_dy)));
6659     should_move_on = (can_move_on &&
6660                       (!can_turn_left ||
6661                        !can_turn_right ||
6662                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6663                                                  y + move_dy + left_dy) ||
6664                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6665                                                  y + move_dy + right_dy)));
6666
6667     if (should_turn_left || should_turn_right || should_move_on)
6668     {
6669       if (should_turn_left && should_turn_right && should_move_on)
6670         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6671                         rnd < 2 * rnd_value / 3 ? right_dir :
6672                         old_move_dir);
6673       else if (should_turn_left && should_turn_right)
6674         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6675       else if (should_turn_left && should_move_on)
6676         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6677       else if (should_turn_right && should_move_on)
6678         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6679       else if (should_turn_left)
6680         MovDir[x][y] = left_dir;
6681       else if (should_turn_right)
6682         MovDir[x][y] = right_dir;
6683       else if (should_move_on)
6684         MovDir[x][y] = old_move_dir;
6685     }
6686     else if (can_move_on && rnd > rnd_value / 8)
6687       MovDir[x][y] = old_move_dir;
6688     else if (can_turn_left && can_turn_right)
6689       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6690     else if (can_turn_left && rnd > rnd_value / 8)
6691       MovDir[x][y] = left_dir;
6692     else if (can_turn_right && rnd > rnd_value/8)
6693       MovDir[x][y] = right_dir;
6694     else
6695       MovDir[x][y] = back_dir;
6696
6697     xx = x + move_xy[MovDir[x][y]].dx;
6698     yy = y + move_xy[MovDir[x][y]].dy;
6699
6700     if (!IN_LEV_FIELD(xx, yy) ||
6701         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6702       MovDir[x][y] = old_move_dir;
6703
6704     MovDelay[x][y] = 0;
6705   }
6706   else if (element == EL_DRAGON)
6707   {
6708     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6709     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6710     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6711     int rnd_value = 24;
6712     int rnd = RND(rnd_value);
6713
6714     if (can_move_on && rnd > rnd_value / 8)
6715       MovDir[x][y] = old_move_dir;
6716     else if (can_turn_left && can_turn_right)
6717       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6718     else if (can_turn_left && rnd > rnd_value / 8)
6719       MovDir[x][y] = left_dir;
6720     else if (can_turn_right && rnd > rnd_value / 8)
6721       MovDir[x][y] = right_dir;
6722     else
6723       MovDir[x][y] = back_dir;
6724
6725     xx = x + move_xy[MovDir[x][y]].dx;
6726     yy = y + move_xy[MovDir[x][y]].dy;
6727
6728     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6729       MovDir[x][y] = old_move_dir;
6730
6731     MovDelay[x][y] = 0;
6732   }
6733   else if (element == EL_MOLE)
6734   {
6735     boolean can_move_on =
6736       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6737                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6738                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6739     if (!can_move_on)
6740     {
6741       boolean can_turn_left =
6742         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6743                               IS_AMOEBOID(Feld[left_x][left_y])));
6744
6745       boolean can_turn_right =
6746         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6747                               IS_AMOEBOID(Feld[right_x][right_y])));
6748
6749       if (can_turn_left && can_turn_right)
6750         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6751       else if (can_turn_left)
6752         MovDir[x][y] = left_dir;
6753       else
6754         MovDir[x][y] = right_dir;
6755     }
6756
6757     if (MovDir[x][y] != old_move_dir)
6758       MovDelay[x][y] = 9;
6759   }
6760   else if (element == EL_BALLOON)
6761   {
6762     MovDir[x][y] = game.wind_direction;
6763     MovDelay[x][y] = 0;
6764   }
6765   else if (element == EL_SPRING)
6766   {
6767     if (MovDir[x][y] & MV_HORIZONTAL)
6768     {
6769       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6770           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6771       {
6772         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6773         ResetGfxAnimation(move_x, move_y);
6774         TEST_DrawLevelField(move_x, move_y);
6775
6776         MovDir[x][y] = back_dir;
6777       }
6778       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6779                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6780         MovDir[x][y] = MV_NONE;
6781     }
6782
6783     MovDelay[x][y] = 0;
6784   }
6785   else if (element == EL_ROBOT ||
6786            element == EL_SATELLITE ||
6787            element == EL_PENGUIN ||
6788            element == EL_EMC_ANDROID)
6789   {
6790     int attr_x = -1, attr_y = -1;
6791
6792     if (AllPlayersGone)
6793     {
6794       attr_x = ExitX;
6795       attr_y = ExitY;
6796     }
6797     else
6798     {
6799       int i;
6800
6801       for (i = 0; i < MAX_PLAYERS; i++)
6802       {
6803         struct PlayerInfo *player = &stored_player[i];
6804         int jx = player->jx, jy = player->jy;
6805
6806         if (!player->active)
6807           continue;
6808
6809         if (attr_x == -1 ||
6810             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6811         {
6812           attr_x = jx;
6813           attr_y = jy;
6814         }
6815       }
6816     }
6817
6818     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6819         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6820          game.engine_version < VERSION_IDENT(3,1,0,0)))
6821     {
6822       attr_x = ZX;
6823       attr_y = ZY;
6824     }
6825
6826     if (element == EL_PENGUIN)
6827     {
6828       int i;
6829       static int xy[4][2] =
6830       {
6831         { 0, -1 },
6832         { -1, 0 },
6833         { +1, 0 },
6834         { 0, +1 }
6835       };
6836
6837       for (i = 0; i < NUM_DIRECTIONS; i++)
6838       {
6839         int ex = x + xy[i][0];
6840         int ey = y + xy[i][1];
6841
6842         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6843                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6844                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6845                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6846         {
6847           attr_x = ex;
6848           attr_y = ey;
6849           break;
6850         }
6851       }
6852     }
6853
6854     MovDir[x][y] = MV_NONE;
6855     if (attr_x < x)
6856       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6857     else if (attr_x > x)
6858       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6859     if (attr_y < y)
6860       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6861     else if (attr_y > y)
6862       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6863
6864     if (element == EL_ROBOT)
6865     {
6866       int newx, newy;
6867
6868       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6869         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6870       Moving2Blocked(x, y, &newx, &newy);
6871
6872       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6873         MovDelay[x][y] = 8 + 8 * !RND(3);
6874       else
6875         MovDelay[x][y] = 16;
6876     }
6877     else if (element == EL_PENGUIN)
6878     {
6879       int newx, newy;
6880
6881       MovDelay[x][y] = 1;
6882
6883       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6884       {
6885         boolean first_horiz = RND(2);
6886         int new_move_dir = MovDir[x][y];
6887
6888         MovDir[x][y] =
6889           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6890         Moving2Blocked(x, y, &newx, &newy);
6891
6892         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6893           return;
6894
6895         MovDir[x][y] =
6896           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6897         Moving2Blocked(x, y, &newx, &newy);
6898
6899         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6900           return;
6901
6902         MovDir[x][y] = old_move_dir;
6903         return;
6904       }
6905     }
6906     else if (element == EL_SATELLITE)
6907     {
6908       int newx, newy;
6909
6910       MovDelay[x][y] = 1;
6911
6912       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6913       {
6914         boolean first_horiz = RND(2);
6915         int new_move_dir = MovDir[x][y];
6916
6917         MovDir[x][y] =
6918           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6919         Moving2Blocked(x, y, &newx, &newy);
6920
6921         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6922           return;
6923
6924         MovDir[x][y] =
6925           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6926         Moving2Blocked(x, y, &newx, &newy);
6927
6928         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6929           return;
6930
6931         MovDir[x][y] = old_move_dir;
6932         return;
6933       }
6934     }
6935     else if (element == EL_EMC_ANDROID)
6936     {
6937       static int check_pos[16] =
6938       {
6939         -1,             //  0 => (invalid)
6940         7,              //  1 => MV_LEFT
6941         3,              //  2 => MV_RIGHT
6942         -1,             //  3 => (invalid)
6943         1,              //  4 =>            MV_UP
6944         0,              //  5 => MV_LEFT  | MV_UP
6945         2,              //  6 => MV_RIGHT | MV_UP
6946         -1,             //  7 => (invalid)
6947         5,              //  8 =>            MV_DOWN
6948         6,              //  9 => MV_LEFT  | MV_DOWN
6949         4,              // 10 => MV_RIGHT | MV_DOWN
6950         -1,             // 11 => (invalid)
6951         -1,             // 12 => (invalid)
6952         -1,             // 13 => (invalid)
6953         -1,             // 14 => (invalid)
6954         -1,             // 15 => (invalid)
6955       };
6956       static struct
6957       {
6958         int dx, dy;
6959         int dir;
6960       } check_xy[8] =
6961       {
6962         { -1, -1,       MV_LEFT  | MV_UP   },
6963         {  0, -1,                  MV_UP   },
6964         { +1, -1,       MV_RIGHT | MV_UP   },
6965         { +1,  0,       MV_RIGHT           },
6966         { +1, +1,       MV_RIGHT | MV_DOWN },
6967         {  0, +1,                  MV_DOWN },
6968         { -1, +1,       MV_LEFT  | MV_DOWN },
6969         { -1,  0,       MV_LEFT            },
6970       };
6971       int start_pos, check_order;
6972       boolean can_clone = FALSE;
6973       int i;
6974
6975       // check if there is any free field around current position
6976       for (i = 0; i < 8; i++)
6977       {
6978         int newx = x + check_xy[i].dx;
6979         int newy = y + check_xy[i].dy;
6980
6981         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6982         {
6983           can_clone = TRUE;
6984
6985           break;
6986         }
6987       }
6988
6989       if (can_clone)            // randomly find an element to clone
6990       {
6991         can_clone = FALSE;
6992
6993         start_pos = check_pos[RND(8)];
6994         check_order = (RND(2) ? -1 : +1);
6995
6996         for (i = 0; i < 8; i++)
6997         {
6998           int pos_raw = start_pos + i * check_order;
6999           int pos = (pos_raw + 8) % 8;
7000           int newx = x + check_xy[pos].dx;
7001           int newy = y + check_xy[pos].dy;
7002
7003           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7004           {
7005             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7006             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7007
7008             Store[x][y] = Feld[newx][newy];
7009
7010             can_clone = TRUE;
7011
7012             break;
7013           }
7014         }
7015       }
7016
7017       if (can_clone)            // randomly find a direction to move
7018       {
7019         can_clone = FALSE;
7020
7021         start_pos = check_pos[RND(8)];
7022         check_order = (RND(2) ? -1 : +1);
7023
7024         for (i = 0; i < 8; i++)
7025         {
7026           int pos_raw = start_pos + i * check_order;
7027           int pos = (pos_raw + 8) % 8;
7028           int newx = x + check_xy[pos].dx;
7029           int newy = y + check_xy[pos].dy;
7030           int new_move_dir = check_xy[pos].dir;
7031
7032           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7033           {
7034             MovDir[x][y] = new_move_dir;
7035             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7036
7037             can_clone = TRUE;
7038
7039             break;
7040           }
7041         }
7042       }
7043
7044       if (can_clone)            // cloning and moving successful
7045         return;
7046
7047       // cannot clone -- try to move towards player
7048
7049       start_pos = check_pos[MovDir[x][y] & 0x0f];
7050       check_order = (RND(2) ? -1 : +1);
7051
7052       for (i = 0; i < 3; i++)
7053       {
7054         // first check start_pos, then previous/next or (next/previous) pos
7055         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7056         int pos = (pos_raw + 8) % 8;
7057         int newx = x + check_xy[pos].dx;
7058         int newy = y + check_xy[pos].dy;
7059         int new_move_dir = check_xy[pos].dir;
7060
7061         if (IS_PLAYER(newx, newy))
7062           break;
7063
7064         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7065         {
7066           MovDir[x][y] = new_move_dir;
7067           MovDelay[x][y] = level.android_move_time * 8 + 1;
7068
7069           break;
7070         }
7071       }
7072     }
7073   }
7074   else if (move_pattern == MV_TURNING_LEFT ||
7075            move_pattern == MV_TURNING_RIGHT ||
7076            move_pattern == MV_TURNING_LEFT_RIGHT ||
7077            move_pattern == MV_TURNING_RIGHT_LEFT ||
7078            move_pattern == MV_TURNING_RANDOM ||
7079            move_pattern == MV_ALL_DIRECTIONS)
7080   {
7081     boolean can_turn_left =
7082       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7083     boolean can_turn_right =
7084       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7085
7086     if (element_info[element].move_stepsize == 0)       // "not moving"
7087       return;
7088
7089     if (move_pattern == MV_TURNING_LEFT)
7090       MovDir[x][y] = left_dir;
7091     else if (move_pattern == MV_TURNING_RIGHT)
7092       MovDir[x][y] = right_dir;
7093     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7094       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7095     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7096       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7097     else if (move_pattern == MV_TURNING_RANDOM)
7098       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7099                       can_turn_right && !can_turn_left ? right_dir :
7100                       RND(2) ? left_dir : right_dir);
7101     else if (can_turn_left && can_turn_right)
7102       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7103     else if (can_turn_left)
7104       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7105     else if (can_turn_right)
7106       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7107     else
7108       MovDir[x][y] = back_dir;
7109
7110     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7111   }
7112   else if (move_pattern == MV_HORIZONTAL ||
7113            move_pattern == MV_VERTICAL)
7114   {
7115     if (move_pattern & old_move_dir)
7116       MovDir[x][y] = back_dir;
7117     else if (move_pattern == MV_HORIZONTAL)
7118       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7119     else if (move_pattern == MV_VERTICAL)
7120       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7121
7122     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7123   }
7124   else if (move_pattern & MV_ANY_DIRECTION)
7125   {
7126     MovDir[x][y] = move_pattern;
7127     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128   }
7129   else if (move_pattern & MV_WIND_DIRECTION)
7130   {
7131     MovDir[x][y] = game.wind_direction;
7132     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7133   }
7134   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7135   {
7136     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7137       MovDir[x][y] = left_dir;
7138     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7139       MovDir[x][y] = right_dir;
7140
7141     if (MovDir[x][y] != old_move_dir)
7142       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7143   }
7144   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7145   {
7146     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7147       MovDir[x][y] = right_dir;
7148     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7149       MovDir[x][y] = left_dir;
7150
7151     if (MovDir[x][y] != old_move_dir)
7152       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7153   }
7154   else if (move_pattern == MV_TOWARDS_PLAYER ||
7155            move_pattern == MV_AWAY_FROM_PLAYER)
7156   {
7157     int attr_x = -1, attr_y = -1;
7158     int newx, newy;
7159     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7160
7161     if (AllPlayersGone)
7162     {
7163       attr_x = ExitX;
7164       attr_y = ExitY;
7165     }
7166     else
7167     {
7168       int i;
7169
7170       for (i = 0; i < MAX_PLAYERS; i++)
7171       {
7172         struct PlayerInfo *player = &stored_player[i];
7173         int jx = player->jx, jy = player->jy;
7174
7175         if (!player->active)
7176           continue;
7177
7178         if (attr_x == -1 ||
7179             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7180         {
7181           attr_x = jx;
7182           attr_y = jy;
7183         }
7184       }
7185     }
7186
7187     MovDir[x][y] = MV_NONE;
7188     if (attr_x < x)
7189       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7190     else if (attr_x > x)
7191       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7192     if (attr_y < y)
7193       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7194     else if (attr_y > y)
7195       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7196
7197     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7198
7199     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7200     {
7201       boolean first_horiz = RND(2);
7202       int new_move_dir = MovDir[x][y];
7203
7204       if (element_info[element].move_stepsize == 0)     // "not moving"
7205       {
7206         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7207         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7208
7209         return;
7210       }
7211
7212       MovDir[x][y] =
7213         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7214       Moving2Blocked(x, y, &newx, &newy);
7215
7216       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7217         return;
7218
7219       MovDir[x][y] =
7220         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7221       Moving2Blocked(x, y, &newx, &newy);
7222
7223       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7224         return;
7225
7226       MovDir[x][y] = old_move_dir;
7227     }
7228   }
7229   else if (move_pattern == MV_WHEN_PUSHED ||
7230            move_pattern == MV_WHEN_DROPPED)
7231   {
7232     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7233       MovDir[x][y] = MV_NONE;
7234
7235     MovDelay[x][y] = 0;
7236   }
7237   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7238   {
7239     static int test_xy[7][2] =
7240     {
7241       { 0, -1 },
7242       { -1, 0 },
7243       { +1, 0 },
7244       { 0, +1 },
7245       { 0, -1 },
7246       { -1, 0 },
7247       { +1, 0 },
7248     };
7249     static int test_dir[7] =
7250     {
7251       MV_UP,
7252       MV_LEFT,
7253       MV_RIGHT,
7254       MV_DOWN,
7255       MV_UP,
7256       MV_LEFT,
7257       MV_RIGHT,
7258     };
7259     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7260     int move_preference = -1000000;     // start with very low preference
7261     int new_move_dir = MV_NONE;
7262     int start_test = RND(4);
7263     int i;
7264
7265     for (i = 0; i < NUM_DIRECTIONS; i++)
7266     {
7267       int move_dir = test_dir[start_test + i];
7268       int move_dir_preference;
7269
7270       xx = x + test_xy[start_test + i][0];
7271       yy = y + test_xy[start_test + i][1];
7272
7273       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7274           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7275       {
7276         new_move_dir = move_dir;
7277
7278         break;
7279       }
7280
7281       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7282         continue;
7283
7284       move_dir_preference = -1 * RunnerVisit[xx][yy];
7285       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7286         move_dir_preference = PlayerVisit[xx][yy];
7287
7288       if (move_dir_preference > move_preference)
7289       {
7290         // prefer field that has not been visited for the longest time
7291         move_preference = move_dir_preference;
7292         new_move_dir = move_dir;
7293       }
7294       else if (move_dir_preference == move_preference &&
7295                move_dir == old_move_dir)
7296       {
7297         // prefer last direction when all directions are preferred equally
7298         move_preference = move_dir_preference;
7299         new_move_dir = move_dir;
7300       }
7301     }
7302
7303     MovDir[x][y] = new_move_dir;
7304     if (old_move_dir != new_move_dir)
7305       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7306   }
7307 }
7308
7309 static void TurnRound(int x, int y)
7310 {
7311   int direction = MovDir[x][y];
7312
7313   TurnRoundExt(x, y);
7314
7315   GfxDir[x][y] = MovDir[x][y];
7316
7317   if (direction != MovDir[x][y])
7318     GfxFrame[x][y] = 0;
7319
7320   if (MovDelay[x][y])
7321     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7322
7323   ResetGfxFrame(x, y);
7324 }
7325
7326 static boolean JustBeingPushed(int x, int y)
7327 {
7328   int i;
7329
7330   for (i = 0; i < MAX_PLAYERS; i++)
7331   {
7332     struct PlayerInfo *player = &stored_player[i];
7333
7334     if (player->active && player->is_pushing && player->MovPos)
7335     {
7336       int next_jx = player->jx + (player->jx - player->last_jx);
7337       int next_jy = player->jy + (player->jy - player->last_jy);
7338
7339       if (x == next_jx && y == next_jy)
7340         return TRUE;
7341     }
7342   }
7343
7344   return FALSE;
7345 }
7346
7347 static void StartMoving(int x, int y)
7348 {
7349   boolean started_moving = FALSE;       // some elements can fall _and_ move
7350   int element = Feld[x][y];
7351
7352   if (Stop[x][y])
7353     return;
7354
7355   if (MovDelay[x][y] == 0)
7356     GfxAction[x][y] = ACTION_DEFAULT;
7357
7358   if (CAN_FALL(element) && y < lev_fieldy - 1)
7359   {
7360     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7361         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7362       if (JustBeingPushed(x, y))
7363         return;
7364
7365     if (element == EL_QUICKSAND_FULL)
7366     {
7367       if (IS_FREE(x, y + 1))
7368       {
7369         InitMovingField(x, y, MV_DOWN);
7370         started_moving = TRUE;
7371
7372         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7373 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7374         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7375           Store[x][y] = EL_ROCK;
7376 #else
7377         Store[x][y] = EL_ROCK;
7378 #endif
7379
7380         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7381       }
7382       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7383       {
7384         if (!MovDelay[x][y])
7385         {
7386           MovDelay[x][y] = TILEY + 1;
7387
7388           ResetGfxAnimation(x, y);
7389           ResetGfxAnimation(x, y + 1);
7390         }
7391
7392         if (MovDelay[x][y])
7393         {
7394           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7395           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7396
7397           MovDelay[x][y]--;
7398           if (MovDelay[x][y])
7399             return;
7400         }
7401
7402         Feld[x][y] = EL_QUICKSAND_EMPTY;
7403         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7404         Store[x][y + 1] = Store[x][y];
7405         Store[x][y] = 0;
7406
7407         PlayLevelSoundAction(x, y, ACTION_FILLING);
7408       }
7409       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7410       {
7411         if (!MovDelay[x][y])
7412         {
7413           MovDelay[x][y] = TILEY + 1;
7414
7415           ResetGfxAnimation(x, y);
7416           ResetGfxAnimation(x, y + 1);
7417         }
7418
7419         if (MovDelay[x][y])
7420         {
7421           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7422           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7423
7424           MovDelay[x][y]--;
7425           if (MovDelay[x][y])
7426             return;
7427         }
7428
7429         Feld[x][y] = EL_QUICKSAND_EMPTY;
7430         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7431         Store[x][y + 1] = Store[x][y];
7432         Store[x][y] = 0;
7433
7434         PlayLevelSoundAction(x, y, ACTION_FILLING);
7435       }
7436     }
7437     else if (element == EL_QUICKSAND_FAST_FULL)
7438     {
7439       if (IS_FREE(x, y + 1))
7440       {
7441         InitMovingField(x, y, MV_DOWN);
7442         started_moving = TRUE;
7443
7444         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7445 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7446         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7447           Store[x][y] = EL_ROCK;
7448 #else
7449         Store[x][y] = EL_ROCK;
7450 #endif
7451
7452         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7453       }
7454       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7455       {
7456         if (!MovDelay[x][y])
7457         {
7458           MovDelay[x][y] = TILEY + 1;
7459
7460           ResetGfxAnimation(x, y);
7461           ResetGfxAnimation(x, y + 1);
7462         }
7463
7464         if (MovDelay[x][y])
7465         {
7466           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7467           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7468
7469           MovDelay[x][y]--;
7470           if (MovDelay[x][y])
7471             return;
7472         }
7473
7474         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7475         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7476         Store[x][y + 1] = Store[x][y];
7477         Store[x][y] = 0;
7478
7479         PlayLevelSoundAction(x, y, ACTION_FILLING);
7480       }
7481       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7482       {
7483         if (!MovDelay[x][y])
7484         {
7485           MovDelay[x][y] = TILEY + 1;
7486
7487           ResetGfxAnimation(x, y);
7488           ResetGfxAnimation(x, y + 1);
7489         }
7490
7491         if (MovDelay[x][y])
7492         {
7493           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7494           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7495
7496           MovDelay[x][y]--;
7497           if (MovDelay[x][y])
7498             return;
7499         }
7500
7501         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7502         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7503         Store[x][y + 1] = Store[x][y];
7504         Store[x][y] = 0;
7505
7506         PlayLevelSoundAction(x, y, ACTION_FILLING);
7507       }
7508     }
7509     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7510              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7511     {
7512       InitMovingField(x, y, MV_DOWN);
7513       started_moving = TRUE;
7514
7515       Feld[x][y] = EL_QUICKSAND_FILLING;
7516       Store[x][y] = element;
7517
7518       PlayLevelSoundAction(x, y, ACTION_FILLING);
7519     }
7520     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7521              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7522     {
7523       InitMovingField(x, y, MV_DOWN);
7524       started_moving = TRUE;
7525
7526       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7527       Store[x][y] = element;
7528
7529       PlayLevelSoundAction(x, y, ACTION_FILLING);
7530     }
7531     else if (element == EL_MAGIC_WALL_FULL)
7532     {
7533       if (IS_FREE(x, y + 1))
7534       {
7535         InitMovingField(x, y, MV_DOWN);
7536         started_moving = TRUE;
7537
7538         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7539         Store[x][y] = EL_CHANGED(Store[x][y]);
7540       }
7541       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7542       {
7543         if (!MovDelay[x][y])
7544           MovDelay[x][y] = TILEY / 4 + 1;
7545
7546         if (MovDelay[x][y])
7547         {
7548           MovDelay[x][y]--;
7549           if (MovDelay[x][y])
7550             return;
7551         }
7552
7553         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7554         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7555         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7556         Store[x][y] = 0;
7557       }
7558     }
7559     else if (element == EL_BD_MAGIC_WALL_FULL)
7560     {
7561       if (IS_FREE(x, y + 1))
7562       {
7563         InitMovingField(x, y, MV_DOWN);
7564         started_moving = TRUE;
7565
7566         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7567         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7568       }
7569       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7570       {
7571         if (!MovDelay[x][y])
7572           MovDelay[x][y] = TILEY / 4 + 1;
7573
7574         if (MovDelay[x][y])
7575         {
7576           MovDelay[x][y]--;
7577           if (MovDelay[x][y])
7578             return;
7579         }
7580
7581         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7582         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7583         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7584         Store[x][y] = 0;
7585       }
7586     }
7587     else if (element == EL_DC_MAGIC_WALL_FULL)
7588     {
7589       if (IS_FREE(x, y + 1))
7590       {
7591         InitMovingField(x, y, MV_DOWN);
7592         started_moving = TRUE;
7593
7594         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7595         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7596       }
7597       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7598       {
7599         if (!MovDelay[x][y])
7600           MovDelay[x][y] = TILEY / 4 + 1;
7601
7602         if (MovDelay[x][y])
7603         {
7604           MovDelay[x][y]--;
7605           if (MovDelay[x][y])
7606             return;
7607         }
7608
7609         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7610         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7611         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7612         Store[x][y] = 0;
7613       }
7614     }
7615     else if ((CAN_PASS_MAGIC_WALL(element) &&
7616               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7617                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7618              (CAN_PASS_DC_MAGIC_WALL(element) &&
7619               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7620
7621     {
7622       InitMovingField(x, y, MV_DOWN);
7623       started_moving = TRUE;
7624
7625       Feld[x][y] =
7626         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7627          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7628          EL_DC_MAGIC_WALL_FILLING);
7629       Store[x][y] = element;
7630     }
7631     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7632     {
7633       SplashAcid(x, y + 1);
7634
7635       InitMovingField(x, y, MV_DOWN);
7636       started_moving = TRUE;
7637
7638       Store[x][y] = EL_ACID;
7639     }
7640     else if (
7641              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7642               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7643              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7644               CAN_FALL(element) && WasJustFalling[x][y] &&
7645               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7646
7647              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7648               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7649               (Feld[x][y + 1] == EL_BLOCKED)))
7650     {
7651       /* this is needed for a special case not covered by calling "Impact()"
7652          from "ContinueMoving()": if an element moves to a tile directly below
7653          another element which was just falling on that tile (which was empty
7654          in the previous frame), the falling element above would just stop
7655          instead of smashing the element below (in previous version, the above
7656          element was just checked for "moving" instead of "falling", resulting
7657          in incorrect smashes caused by horizontal movement of the above
7658          element; also, the case of the player being the element to smash was
7659          simply not covered here... :-/ ) */
7660
7661       CheckCollision[x][y] = 0;
7662       CheckImpact[x][y] = 0;
7663
7664       Impact(x, y);
7665     }
7666     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7667     {
7668       if (MovDir[x][y] == MV_NONE)
7669       {
7670         InitMovingField(x, y, MV_DOWN);
7671         started_moving = TRUE;
7672       }
7673     }
7674     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7675     {
7676       if (WasJustFalling[x][y]) // prevent animation from being restarted
7677         MovDir[x][y] = MV_DOWN;
7678
7679       InitMovingField(x, y, MV_DOWN);
7680       started_moving = TRUE;
7681     }
7682     else if (element == EL_AMOEBA_DROP)
7683     {
7684       Feld[x][y] = EL_AMOEBA_GROWING;
7685       Store[x][y] = EL_AMOEBA_WET;
7686     }
7687     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7688               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7689              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7690              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7691     {
7692       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7693                                 (IS_FREE(x - 1, y + 1) ||
7694                                  Feld[x - 1][y + 1] == EL_ACID));
7695       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7696                                 (IS_FREE(x + 1, y + 1) ||
7697                                  Feld[x + 1][y + 1] == EL_ACID));
7698       boolean can_fall_any  = (can_fall_left || can_fall_right);
7699       boolean can_fall_both = (can_fall_left && can_fall_right);
7700       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7701
7702       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7703       {
7704         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7705           can_fall_right = FALSE;
7706         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7707           can_fall_left = FALSE;
7708         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7709           can_fall_right = FALSE;
7710         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7711           can_fall_left = FALSE;
7712
7713         can_fall_any  = (can_fall_left || can_fall_right);
7714         can_fall_both = FALSE;
7715       }
7716
7717       if (can_fall_both)
7718       {
7719         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7720           can_fall_right = FALSE;       // slip down on left side
7721         else
7722           can_fall_left = !(can_fall_right = RND(2));
7723
7724         can_fall_both = FALSE;
7725       }
7726
7727       if (can_fall_any)
7728       {
7729         // if not determined otherwise, prefer left side for slipping down
7730         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7731         started_moving = TRUE;
7732       }
7733     }
7734     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7735     {
7736       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7737       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7738       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7739       int belt_dir = game.belt_dir[belt_nr];
7740
7741       if ((belt_dir == MV_LEFT  && left_is_free) ||
7742           (belt_dir == MV_RIGHT && right_is_free))
7743       {
7744         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7745
7746         InitMovingField(x, y, belt_dir);
7747         started_moving = TRUE;
7748
7749         Pushed[x][y] = TRUE;
7750         Pushed[nextx][y] = TRUE;
7751
7752         GfxAction[x][y] = ACTION_DEFAULT;
7753       }
7754       else
7755       {
7756         MovDir[x][y] = 0;       // if element was moving, stop it
7757       }
7758     }
7759   }
7760
7761   // not "else if" because of elements that can fall and move (EL_SPRING)
7762   if (CAN_MOVE(element) && !started_moving)
7763   {
7764     int move_pattern = element_info[element].move_pattern;
7765     int newx, newy;
7766
7767     Moving2Blocked(x, y, &newx, &newy);
7768
7769     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7770       return;
7771
7772     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7773         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7774     {
7775       WasJustMoving[x][y] = 0;
7776       CheckCollision[x][y] = 0;
7777
7778       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7779
7780       if (Feld[x][y] != element)        // element has changed
7781         return;
7782     }
7783
7784     if (!MovDelay[x][y])        // start new movement phase
7785     {
7786       // all objects that can change their move direction after each step
7787       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7788
7789       if (element != EL_YAMYAM &&
7790           element != EL_DARK_YAMYAM &&
7791           element != EL_PACMAN &&
7792           !(move_pattern & MV_ANY_DIRECTION) &&
7793           move_pattern != MV_TURNING_LEFT &&
7794           move_pattern != MV_TURNING_RIGHT &&
7795           move_pattern != MV_TURNING_LEFT_RIGHT &&
7796           move_pattern != MV_TURNING_RIGHT_LEFT &&
7797           move_pattern != MV_TURNING_RANDOM)
7798       {
7799         TurnRound(x, y);
7800
7801         if (MovDelay[x][y] && (element == EL_BUG ||
7802                                element == EL_SPACESHIP ||
7803                                element == EL_SP_SNIKSNAK ||
7804                                element == EL_SP_ELECTRON ||
7805                                element == EL_MOLE))
7806           TEST_DrawLevelField(x, y);
7807       }
7808     }
7809
7810     if (MovDelay[x][y])         // wait some time before next movement
7811     {
7812       MovDelay[x][y]--;
7813
7814       if (element == EL_ROBOT ||
7815           element == EL_YAMYAM ||
7816           element == EL_DARK_YAMYAM)
7817       {
7818         DrawLevelElementAnimationIfNeeded(x, y, element);
7819         PlayLevelSoundAction(x, y, ACTION_WAITING);
7820       }
7821       else if (element == EL_SP_ELECTRON)
7822         DrawLevelElementAnimationIfNeeded(x, y, element);
7823       else if (element == EL_DRAGON)
7824       {
7825         int i;
7826         int dir = MovDir[x][y];
7827         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7828         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7829         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7830                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7831                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7832                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7833         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7834
7835         GfxAction[x][y] = ACTION_ATTACKING;
7836
7837         if (IS_PLAYER(x, y))
7838           DrawPlayerField(x, y);
7839         else
7840           TEST_DrawLevelField(x, y);
7841
7842         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7843
7844         for (i = 1; i <= 3; i++)
7845         {
7846           int xx = x + i * dx;
7847           int yy = y + i * dy;
7848           int sx = SCREENX(xx);
7849           int sy = SCREENY(yy);
7850           int flame_graphic = graphic + (i - 1);
7851
7852           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7853             break;
7854
7855           if (MovDelay[x][y])
7856           {
7857             int flamed = MovingOrBlocked2Element(xx, yy);
7858
7859             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7860               Bang(xx, yy);
7861             else
7862               RemoveMovingField(xx, yy);
7863
7864             ChangeDelay[xx][yy] = 0;
7865
7866             Feld[xx][yy] = EL_FLAMES;
7867
7868             if (IN_SCR_FIELD(sx, sy))
7869             {
7870               TEST_DrawLevelFieldCrumbled(xx, yy);
7871               DrawGraphic(sx, sy, flame_graphic, frame);
7872             }
7873           }
7874           else
7875           {
7876             if (Feld[xx][yy] == EL_FLAMES)
7877               Feld[xx][yy] = EL_EMPTY;
7878             TEST_DrawLevelField(xx, yy);
7879           }
7880         }
7881       }
7882
7883       if (MovDelay[x][y])       // element still has to wait some time
7884       {
7885         PlayLevelSoundAction(x, y, ACTION_WAITING);
7886
7887         return;
7888       }
7889     }
7890
7891     // now make next step
7892
7893     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7894
7895     if (DONT_COLLIDE_WITH(element) &&
7896         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7897         !PLAYER_ENEMY_PROTECTED(newx, newy))
7898     {
7899       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7900
7901       return;
7902     }
7903
7904     else if (CAN_MOVE_INTO_ACID(element) &&
7905              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7906              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7907              (MovDir[x][y] == MV_DOWN ||
7908               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7909     {
7910       SplashAcid(newx, newy);
7911       Store[x][y] = EL_ACID;
7912     }
7913     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7914     {
7915       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7916           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7917           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7918           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7919       {
7920         RemoveField(x, y);
7921         TEST_DrawLevelField(x, y);
7922
7923         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7924         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7925           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7926
7927         local_player->friends_still_needed--;
7928         if (!local_player->friends_still_needed &&
7929             !local_player->GameOver && AllPlayersGone)
7930           PlayerWins(local_player);
7931
7932         return;
7933       }
7934       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7935       {
7936         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7937           TEST_DrawLevelField(newx, newy);
7938         else
7939           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7940       }
7941       else if (!IS_FREE(newx, newy))
7942       {
7943         GfxAction[x][y] = ACTION_WAITING;
7944
7945         if (IS_PLAYER(x, y))
7946           DrawPlayerField(x, y);
7947         else
7948           TEST_DrawLevelField(x, y);
7949
7950         return;
7951       }
7952     }
7953     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7954     {
7955       if (IS_FOOD_PIG(Feld[newx][newy]))
7956       {
7957         if (IS_MOVING(newx, newy))
7958           RemoveMovingField(newx, newy);
7959         else
7960         {
7961           Feld[newx][newy] = EL_EMPTY;
7962           TEST_DrawLevelField(newx, newy);
7963         }
7964
7965         PlayLevelSound(x, y, SND_PIG_DIGGING);
7966       }
7967       else if (!IS_FREE(newx, newy))
7968       {
7969         if (IS_PLAYER(x, y))
7970           DrawPlayerField(x, y);
7971         else
7972           TEST_DrawLevelField(x, y);
7973
7974         return;
7975       }
7976     }
7977     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7978     {
7979       if (Store[x][y] != EL_EMPTY)
7980       {
7981         boolean can_clone = FALSE;
7982         int xx, yy;
7983
7984         // check if element to clone is still there
7985         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7986         {
7987           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7988           {
7989             can_clone = TRUE;
7990
7991             break;
7992           }
7993         }
7994
7995         // cannot clone or target field not free anymore -- do not clone
7996         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7997           Store[x][y] = EL_EMPTY;
7998       }
7999
8000       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8001       {
8002         if (IS_MV_DIAGONAL(MovDir[x][y]))
8003         {
8004           int diagonal_move_dir = MovDir[x][y];
8005           int stored = Store[x][y];
8006           int change_delay = 8;
8007           int graphic;
8008
8009           // android is moving diagonally
8010
8011           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8012
8013           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8014           GfxElement[x][y] = EL_EMC_ANDROID;
8015           GfxAction[x][y] = ACTION_SHRINKING;
8016           GfxDir[x][y] = diagonal_move_dir;
8017           ChangeDelay[x][y] = change_delay;
8018
8019           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8020                                    GfxDir[x][y]);
8021
8022           DrawLevelGraphicAnimation(x, y, graphic);
8023           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8024
8025           if (Feld[newx][newy] == EL_ACID)
8026           {
8027             SplashAcid(newx, newy);
8028
8029             return;
8030           }
8031
8032           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8033
8034           Store[newx][newy] = EL_EMC_ANDROID;
8035           GfxElement[newx][newy] = EL_EMC_ANDROID;
8036           GfxAction[newx][newy] = ACTION_GROWING;
8037           GfxDir[newx][newy] = diagonal_move_dir;
8038           ChangeDelay[newx][newy] = change_delay;
8039
8040           graphic = el_act_dir2img(GfxElement[newx][newy],
8041                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8042
8043           DrawLevelGraphicAnimation(newx, newy, graphic);
8044           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8045
8046           return;
8047         }
8048         else
8049         {
8050           Feld[newx][newy] = EL_EMPTY;
8051           TEST_DrawLevelField(newx, newy);
8052
8053           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8054         }
8055       }
8056       else if (!IS_FREE(newx, newy))
8057       {
8058         return;
8059       }
8060     }
8061     else if (IS_CUSTOM_ELEMENT(element) &&
8062              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8063     {
8064       if (!DigFieldByCE(newx, newy, element))
8065         return;
8066
8067       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8068       {
8069         RunnerVisit[x][y] = FrameCounter;
8070         PlayerVisit[x][y] /= 8;         // expire player visit path
8071       }
8072     }
8073     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8074     {
8075       if (!IS_FREE(newx, newy))
8076       {
8077         if (IS_PLAYER(x, y))
8078           DrawPlayerField(x, y);
8079         else
8080           TEST_DrawLevelField(x, y);
8081
8082         return;
8083       }
8084       else
8085       {
8086         boolean wanna_flame = !RND(10);
8087         int dx = newx - x, dy = newy - y;
8088         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8089         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8090         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8091                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8092         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8093                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8094
8095         if ((wanna_flame ||
8096              IS_CLASSIC_ENEMY(element1) ||
8097              IS_CLASSIC_ENEMY(element2)) &&
8098             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8099             element1 != EL_FLAMES && element2 != EL_FLAMES)
8100         {
8101           ResetGfxAnimation(x, y);
8102           GfxAction[x][y] = ACTION_ATTACKING;
8103
8104           if (IS_PLAYER(x, y))
8105             DrawPlayerField(x, y);
8106           else
8107             TEST_DrawLevelField(x, y);
8108
8109           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8110
8111           MovDelay[x][y] = 50;
8112
8113           Feld[newx][newy] = EL_FLAMES;
8114           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8115             Feld[newx1][newy1] = EL_FLAMES;
8116           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8117             Feld[newx2][newy2] = EL_FLAMES;
8118
8119           return;
8120         }
8121       }
8122     }
8123     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8124              Feld[newx][newy] == EL_DIAMOND)
8125     {
8126       if (IS_MOVING(newx, newy))
8127         RemoveMovingField(newx, newy);
8128       else
8129       {
8130         Feld[newx][newy] = EL_EMPTY;
8131         TEST_DrawLevelField(newx, newy);
8132       }
8133
8134       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8135     }
8136     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8137              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8138     {
8139       if (AmoebaNr[newx][newy])
8140       {
8141         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8142         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8143             Feld[newx][newy] == EL_BD_AMOEBA)
8144           AmoebaCnt[AmoebaNr[newx][newy]]--;
8145       }
8146
8147       if (IS_MOVING(newx, newy))
8148       {
8149         RemoveMovingField(newx, newy);
8150       }
8151       else
8152       {
8153         Feld[newx][newy] = EL_EMPTY;
8154         TEST_DrawLevelField(newx, newy);
8155       }
8156
8157       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8158     }
8159     else if ((element == EL_PACMAN || element == EL_MOLE)
8160              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8161     {
8162       if (AmoebaNr[newx][newy])
8163       {
8164         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8165         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8166             Feld[newx][newy] == EL_BD_AMOEBA)
8167           AmoebaCnt[AmoebaNr[newx][newy]]--;
8168       }
8169
8170       if (element == EL_MOLE)
8171       {
8172         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8173         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8174
8175         ResetGfxAnimation(x, y);
8176         GfxAction[x][y] = ACTION_DIGGING;
8177         TEST_DrawLevelField(x, y);
8178
8179         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8180
8181         return;                         // wait for shrinking amoeba
8182       }
8183       else      // element == EL_PACMAN
8184       {
8185         Feld[newx][newy] = EL_EMPTY;
8186         TEST_DrawLevelField(newx, newy);
8187         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8188       }
8189     }
8190     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8191              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8192               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8193     {
8194       // wait for shrinking amoeba to completely disappear
8195       return;
8196     }
8197     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8198     {
8199       // object was running against a wall
8200
8201       TurnRound(x, y);
8202
8203       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8204         DrawLevelElementAnimation(x, y, element);
8205
8206       if (DONT_TOUCH(element))
8207         TestIfBadThingTouchesPlayer(x, y);
8208
8209       return;
8210     }
8211
8212     InitMovingField(x, y, MovDir[x][y]);
8213
8214     PlayLevelSoundAction(x, y, ACTION_MOVING);
8215   }
8216
8217   if (MovDir[x][y])
8218     ContinueMoving(x, y);
8219 }
8220
8221 void ContinueMoving(int x, int y)
8222 {
8223   int element = Feld[x][y];
8224   struct ElementInfo *ei = &element_info[element];
8225   int direction = MovDir[x][y];
8226   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8227   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8228   int newx = x + dx, newy = y + dy;
8229   int stored = Store[x][y];
8230   int stored_new = Store[newx][newy];
8231   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8232   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8233   boolean last_line = (newy == lev_fieldy - 1);
8234
8235   MovPos[x][y] += getElementMoveStepsize(x, y);
8236
8237   if (pushed_by_player) // special case: moving object pushed by player
8238     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8239
8240   if (ABS(MovPos[x][y]) < TILEX)
8241   {
8242     TEST_DrawLevelField(x, y);
8243
8244     return;     // element is still moving
8245   }
8246
8247   // element reached destination field
8248
8249   Feld[x][y] = EL_EMPTY;
8250   Feld[newx][newy] = element;
8251   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8252
8253   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8254   {
8255     element = Feld[newx][newy] = EL_ACID;
8256   }
8257   else if (element == EL_MOLE)
8258   {
8259     Feld[x][y] = EL_SAND;
8260
8261     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8262   }
8263   else if (element == EL_QUICKSAND_FILLING)
8264   {
8265     element = Feld[newx][newy] = get_next_element(element);
8266     Store[newx][newy] = Store[x][y];
8267   }
8268   else if (element == EL_QUICKSAND_EMPTYING)
8269   {
8270     Feld[x][y] = get_next_element(element);
8271     element = Feld[newx][newy] = Store[x][y];
8272   }
8273   else if (element == EL_QUICKSAND_FAST_FILLING)
8274   {
8275     element = Feld[newx][newy] = get_next_element(element);
8276     Store[newx][newy] = Store[x][y];
8277   }
8278   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8279   {
8280     Feld[x][y] = get_next_element(element);
8281     element = Feld[newx][newy] = Store[x][y];
8282   }
8283   else if (element == EL_MAGIC_WALL_FILLING)
8284   {
8285     element = Feld[newx][newy] = get_next_element(element);
8286     if (!game.magic_wall_active)
8287       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8288     Store[newx][newy] = Store[x][y];
8289   }
8290   else if (element == EL_MAGIC_WALL_EMPTYING)
8291   {
8292     Feld[x][y] = get_next_element(element);
8293     if (!game.magic_wall_active)
8294       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8295     element = Feld[newx][newy] = Store[x][y];
8296
8297     InitField(newx, newy, FALSE);
8298   }
8299   else if (element == EL_BD_MAGIC_WALL_FILLING)
8300   {
8301     element = Feld[newx][newy] = get_next_element(element);
8302     if (!game.magic_wall_active)
8303       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8304     Store[newx][newy] = Store[x][y];
8305   }
8306   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8307   {
8308     Feld[x][y] = get_next_element(element);
8309     if (!game.magic_wall_active)
8310       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8311     element = Feld[newx][newy] = Store[x][y];
8312
8313     InitField(newx, newy, FALSE);
8314   }
8315   else if (element == EL_DC_MAGIC_WALL_FILLING)
8316   {
8317     element = Feld[newx][newy] = get_next_element(element);
8318     if (!game.magic_wall_active)
8319       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8320     Store[newx][newy] = Store[x][y];
8321   }
8322   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8323   {
8324     Feld[x][y] = get_next_element(element);
8325     if (!game.magic_wall_active)
8326       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8327     element = Feld[newx][newy] = Store[x][y];
8328
8329     InitField(newx, newy, FALSE);
8330   }
8331   else if (element == EL_AMOEBA_DROPPING)
8332   {
8333     Feld[x][y] = get_next_element(element);
8334     element = Feld[newx][newy] = Store[x][y];
8335   }
8336   else if (element == EL_SOKOBAN_OBJECT)
8337   {
8338     if (Back[x][y])
8339       Feld[x][y] = Back[x][y];
8340
8341     if (Back[newx][newy])
8342       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8343
8344     Back[x][y] = Back[newx][newy] = 0;
8345   }
8346
8347   Store[x][y] = EL_EMPTY;
8348   MovPos[x][y] = 0;
8349   MovDir[x][y] = 0;
8350   MovDelay[x][y] = 0;
8351
8352   MovDelay[newx][newy] = 0;
8353
8354   if (CAN_CHANGE_OR_HAS_ACTION(element))
8355   {
8356     // copy element change control values to new field
8357     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8358     ChangePage[newx][newy]  = ChangePage[x][y];
8359     ChangeCount[newx][newy] = ChangeCount[x][y];
8360     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8361   }
8362
8363   CustomValue[newx][newy] = CustomValue[x][y];
8364
8365   ChangeDelay[x][y] = 0;
8366   ChangePage[x][y] = -1;
8367   ChangeCount[x][y] = 0;
8368   ChangeEvent[x][y] = -1;
8369
8370   CustomValue[x][y] = 0;
8371
8372   // copy animation control values to new field
8373   GfxFrame[newx][newy]  = GfxFrame[x][y];
8374   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8375   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8376   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8377
8378   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8379
8380   // some elements can leave other elements behind after moving
8381   if (ei->move_leave_element != EL_EMPTY &&
8382       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8383       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8384   {
8385     int move_leave_element = ei->move_leave_element;
8386
8387     // this makes it possible to leave the removed element again
8388     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8389       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8390
8391     Feld[x][y] = move_leave_element;
8392
8393     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8394       MovDir[x][y] = direction;
8395
8396     InitField(x, y, FALSE);
8397
8398     if (GFX_CRUMBLED(Feld[x][y]))
8399       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8400
8401     if (ELEM_IS_PLAYER(move_leave_element))
8402       RelocatePlayer(x, y, move_leave_element);
8403   }
8404
8405   // do this after checking for left-behind element
8406   ResetGfxAnimation(x, y);      // reset animation values for old field
8407
8408   if (!CAN_MOVE(element) ||
8409       (CAN_FALL(element) && direction == MV_DOWN &&
8410        (element == EL_SPRING ||
8411         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8412         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8413     GfxDir[x][y] = MovDir[newx][newy] = 0;
8414
8415   TEST_DrawLevelField(x, y);
8416   TEST_DrawLevelField(newx, newy);
8417
8418   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8419
8420   // prevent pushed element from moving on in pushed direction
8421   if (pushed_by_player && CAN_MOVE(element) &&
8422       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8423       !(element_info[element].move_pattern & direction))
8424     TurnRound(newx, newy);
8425
8426   // prevent elements on conveyor belt from moving on in last direction
8427   if (pushed_by_conveyor && CAN_FALL(element) &&
8428       direction & MV_HORIZONTAL)
8429     MovDir[newx][newy] = 0;
8430
8431   if (!pushed_by_player)
8432   {
8433     int nextx = newx + dx, nexty = newy + dy;
8434     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8435
8436     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8437
8438     if (CAN_FALL(element) && direction == MV_DOWN)
8439       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8440
8441     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8442       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8443
8444     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8445       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8446   }
8447
8448   if (DONT_TOUCH(element))      // object may be nasty to player or others
8449   {
8450     TestIfBadThingTouchesPlayer(newx, newy);
8451     TestIfBadThingTouchesFriend(newx, newy);
8452
8453     if (!IS_CUSTOM_ELEMENT(element))
8454       TestIfBadThingTouchesOtherBadThing(newx, newy);
8455   }
8456   else if (element == EL_PENGUIN)
8457     TestIfFriendTouchesBadThing(newx, newy);
8458
8459   if (DONT_GET_HIT_BY(element))
8460   {
8461     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8462   }
8463
8464   // give the player one last chance (one more frame) to move away
8465   if (CAN_FALL(element) && direction == MV_DOWN &&
8466       (last_line || (!IS_FREE(x, newy + 1) &&
8467                      (!IS_PLAYER(x, newy + 1) ||
8468                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8469     Impact(x, newy);
8470
8471   if (pushed_by_player && !game.use_change_when_pushing_bug)
8472   {
8473     int push_side = MV_DIR_OPPOSITE(direction);
8474     struct PlayerInfo *player = PLAYERINFO(x, y);
8475
8476     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8477                                player->index_bit, push_side);
8478     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8479                                         player->index_bit, push_side);
8480   }
8481
8482   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8483     MovDelay[newx][newy] = 1;
8484
8485   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8486
8487   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8488   TestIfElementHitsCustomElement(newx, newy, direction);
8489   TestIfPlayerTouchesCustomElement(newx, newy);
8490   TestIfElementTouchesCustomElement(newx, newy);
8491
8492   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8493       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8494     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8495                              MV_DIR_OPPOSITE(direction));
8496 }
8497
8498 int AmoebeNachbarNr(int ax, int ay)
8499 {
8500   int i;
8501   int element = Feld[ax][ay];
8502   int group_nr = 0;
8503   static int xy[4][2] =
8504   {
8505     { 0, -1 },
8506     { -1, 0 },
8507     { +1, 0 },
8508     { 0, +1 }
8509   };
8510
8511   for (i = 0; i < NUM_DIRECTIONS; i++)
8512   {
8513     int x = ax + xy[i][0];
8514     int y = ay + xy[i][1];
8515
8516     if (!IN_LEV_FIELD(x, y))
8517       continue;
8518
8519     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8520       group_nr = AmoebaNr[x][y];
8521   }
8522
8523   return group_nr;
8524 }
8525
8526 static void AmoebenVereinigen(int ax, int ay)
8527 {
8528   int i, x, y, xx, yy;
8529   int new_group_nr = AmoebaNr[ax][ay];
8530   static int xy[4][2] =
8531   {
8532     { 0, -1 },
8533     { -1, 0 },
8534     { +1, 0 },
8535     { 0, +1 }
8536   };
8537
8538   if (new_group_nr == 0)
8539     return;
8540
8541   for (i = 0; i < NUM_DIRECTIONS; i++)
8542   {
8543     x = ax + xy[i][0];
8544     y = ay + xy[i][1];
8545
8546     if (!IN_LEV_FIELD(x, y))
8547       continue;
8548
8549     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8550          Feld[x][y] == EL_BD_AMOEBA ||
8551          Feld[x][y] == EL_AMOEBA_DEAD) &&
8552         AmoebaNr[x][y] != new_group_nr)
8553     {
8554       int old_group_nr = AmoebaNr[x][y];
8555
8556       if (old_group_nr == 0)
8557         return;
8558
8559       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8560       AmoebaCnt[old_group_nr] = 0;
8561       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8562       AmoebaCnt2[old_group_nr] = 0;
8563
8564       SCAN_PLAYFIELD(xx, yy)
8565       {
8566         if (AmoebaNr[xx][yy] == old_group_nr)
8567           AmoebaNr[xx][yy] = new_group_nr;
8568       }
8569     }
8570   }
8571 }
8572
8573 void AmoebeUmwandeln(int ax, int ay)
8574 {
8575   int i, x, y;
8576
8577   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8578   {
8579     int group_nr = AmoebaNr[ax][ay];
8580
8581 #ifdef DEBUG
8582     if (group_nr == 0)
8583     {
8584       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8585       printf("AmoebeUmwandeln(): This should never happen!\n");
8586       return;
8587     }
8588 #endif
8589
8590     SCAN_PLAYFIELD(x, y)
8591     {
8592       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8593       {
8594         AmoebaNr[x][y] = 0;
8595         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8596       }
8597     }
8598
8599     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8600                             SND_AMOEBA_TURNING_TO_GEM :
8601                             SND_AMOEBA_TURNING_TO_ROCK));
8602     Bang(ax, ay);
8603   }
8604   else
8605   {
8606     static int xy[4][2] =
8607     {
8608       { 0, -1 },
8609       { -1, 0 },
8610       { +1, 0 },
8611       { 0, +1 }
8612     };
8613
8614     for (i = 0; i < NUM_DIRECTIONS; i++)
8615     {
8616       x = ax + xy[i][0];
8617       y = ay + xy[i][1];
8618
8619       if (!IN_LEV_FIELD(x, y))
8620         continue;
8621
8622       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8623       {
8624         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8625                               SND_AMOEBA_TURNING_TO_GEM :
8626                               SND_AMOEBA_TURNING_TO_ROCK));
8627         Bang(x, y);
8628       }
8629     }
8630   }
8631 }
8632
8633 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8634 {
8635   int x, y;
8636   int group_nr = AmoebaNr[ax][ay];
8637   boolean done = FALSE;
8638
8639 #ifdef DEBUG
8640   if (group_nr == 0)
8641   {
8642     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8643     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8644     return;
8645   }
8646 #endif
8647
8648   SCAN_PLAYFIELD(x, y)
8649   {
8650     if (AmoebaNr[x][y] == group_nr &&
8651         (Feld[x][y] == EL_AMOEBA_DEAD ||
8652          Feld[x][y] == EL_BD_AMOEBA ||
8653          Feld[x][y] == EL_AMOEBA_GROWING))
8654     {
8655       AmoebaNr[x][y] = 0;
8656       Feld[x][y] = new_element;
8657       InitField(x, y, FALSE);
8658       TEST_DrawLevelField(x, y);
8659       done = TRUE;
8660     }
8661   }
8662
8663   if (done)
8664     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8665                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8666                             SND_BD_AMOEBA_TURNING_TO_GEM));
8667 }
8668
8669 static void AmoebeWaechst(int x, int y)
8670 {
8671   static unsigned int sound_delay = 0;
8672   static unsigned int sound_delay_value = 0;
8673
8674   if (!MovDelay[x][y])          // start new growing cycle
8675   {
8676     MovDelay[x][y] = 7;
8677
8678     if (DelayReached(&sound_delay, sound_delay_value))
8679     {
8680       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8681       sound_delay_value = 30;
8682     }
8683   }
8684
8685   if (MovDelay[x][y])           // wait some time before growing bigger
8686   {
8687     MovDelay[x][y]--;
8688     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8689     {
8690       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8691                                            6 - MovDelay[x][y]);
8692
8693       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8694     }
8695
8696     if (!MovDelay[x][y])
8697     {
8698       Feld[x][y] = Store[x][y];
8699       Store[x][y] = 0;
8700       TEST_DrawLevelField(x, y);
8701     }
8702   }
8703 }
8704
8705 static void AmoebaDisappearing(int x, int y)
8706 {
8707   static unsigned int sound_delay = 0;
8708   static unsigned int sound_delay_value = 0;
8709
8710   if (!MovDelay[x][y])          // start new shrinking cycle
8711   {
8712     MovDelay[x][y] = 7;
8713
8714     if (DelayReached(&sound_delay, sound_delay_value))
8715       sound_delay_value = 30;
8716   }
8717
8718   if (MovDelay[x][y])           // wait some time before shrinking
8719   {
8720     MovDelay[x][y]--;
8721     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8722     {
8723       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8724                                            6 - MovDelay[x][y]);
8725
8726       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8727     }
8728
8729     if (!MovDelay[x][y])
8730     {
8731       Feld[x][y] = EL_EMPTY;
8732       TEST_DrawLevelField(x, y);
8733
8734       // don't let mole enter this field in this cycle;
8735       // (give priority to objects falling to this field from above)
8736       Stop[x][y] = TRUE;
8737     }
8738   }
8739 }
8740
8741 static void AmoebeAbleger(int ax, int ay)
8742 {
8743   int i;
8744   int element = Feld[ax][ay];
8745   int graphic = el2img(element);
8746   int newax = ax, neway = ay;
8747   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8748   static int xy[4][2] =
8749   {
8750     { 0, -1 },
8751     { -1, 0 },
8752     { +1, 0 },
8753     { 0, +1 }
8754   };
8755
8756   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8757   {
8758     Feld[ax][ay] = EL_AMOEBA_DEAD;
8759     TEST_DrawLevelField(ax, ay);
8760     return;
8761   }
8762
8763   if (IS_ANIMATED(graphic))
8764     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8765
8766   if (!MovDelay[ax][ay])        // start making new amoeba field
8767     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8768
8769   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8770   {
8771     MovDelay[ax][ay]--;
8772     if (MovDelay[ax][ay])
8773       return;
8774   }
8775
8776   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8777   {
8778     int start = RND(4);
8779     int x = ax + xy[start][0];
8780     int y = ay + xy[start][1];
8781
8782     if (!IN_LEV_FIELD(x, y))
8783       return;
8784
8785     if (IS_FREE(x, y) ||
8786         CAN_GROW_INTO(Feld[x][y]) ||
8787         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8788         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8789     {
8790       newax = x;
8791       neway = y;
8792     }
8793
8794     if (newax == ax && neway == ay)
8795       return;
8796   }
8797   else                          // normal or "filled" (BD style) amoeba
8798   {
8799     int start = RND(4);
8800     boolean waiting_for_player = FALSE;
8801
8802     for (i = 0; i < NUM_DIRECTIONS; i++)
8803     {
8804       int j = (start + i) % 4;
8805       int x = ax + xy[j][0];
8806       int y = ay + xy[j][1];
8807
8808       if (!IN_LEV_FIELD(x, y))
8809         continue;
8810
8811       if (IS_FREE(x, y) ||
8812           CAN_GROW_INTO(Feld[x][y]) ||
8813           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8814           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8815       {
8816         newax = x;
8817         neway = y;
8818         break;
8819       }
8820       else if (IS_PLAYER(x, y))
8821         waiting_for_player = TRUE;
8822     }
8823
8824     if (newax == ax && neway == ay)             // amoeba cannot grow
8825     {
8826       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8827       {
8828         Feld[ax][ay] = EL_AMOEBA_DEAD;
8829         TEST_DrawLevelField(ax, ay);
8830         AmoebaCnt[AmoebaNr[ax][ay]]--;
8831
8832         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8833         {
8834           if (element == EL_AMOEBA_FULL)
8835             AmoebeUmwandeln(ax, ay);
8836           else if (element == EL_BD_AMOEBA)
8837             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8838         }
8839       }
8840       return;
8841     }
8842     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8843     {
8844       // amoeba gets larger by growing in some direction
8845
8846       int new_group_nr = AmoebaNr[ax][ay];
8847
8848 #ifdef DEBUG
8849   if (new_group_nr == 0)
8850   {
8851     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8852     printf("AmoebeAbleger(): This should never happen!\n");
8853     return;
8854   }
8855 #endif
8856
8857       AmoebaNr[newax][neway] = new_group_nr;
8858       AmoebaCnt[new_group_nr]++;
8859       AmoebaCnt2[new_group_nr]++;
8860
8861       // if amoeba touches other amoeba(s) after growing, unify them
8862       AmoebenVereinigen(newax, neway);
8863
8864       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8865       {
8866         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8867         return;
8868       }
8869     }
8870   }
8871
8872   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8873       (neway == lev_fieldy - 1 && newax != ax))
8874   {
8875     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8876     Store[newax][neway] = element;
8877   }
8878   else if (neway == ay || element == EL_EMC_DRIPPER)
8879   {
8880     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8881
8882     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8883   }
8884   else
8885   {
8886     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8887     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8888     Store[ax][ay] = EL_AMOEBA_DROP;
8889     ContinueMoving(ax, ay);
8890     return;
8891   }
8892
8893   TEST_DrawLevelField(newax, neway);
8894 }
8895
8896 static void Life(int ax, int ay)
8897 {
8898   int x1, y1, x2, y2;
8899   int life_time = 40;
8900   int element = Feld[ax][ay];
8901   int graphic = el2img(element);
8902   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8903                          level.biomaze);
8904   boolean changed = FALSE;
8905
8906   if (IS_ANIMATED(graphic))
8907     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8908
8909   if (Stop[ax][ay])
8910     return;
8911
8912   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8913     MovDelay[ax][ay] = life_time;
8914
8915   if (MovDelay[ax][ay])         // wait some time before next cycle
8916   {
8917     MovDelay[ax][ay]--;
8918     if (MovDelay[ax][ay])
8919       return;
8920   }
8921
8922   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8923   {
8924     int xx = ax+x1, yy = ay+y1;
8925     int old_element = Feld[xx][yy];
8926     int num_neighbours = 0;
8927
8928     if (!IN_LEV_FIELD(xx, yy))
8929       continue;
8930
8931     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8932     {
8933       int x = xx+x2, y = yy+y2;
8934
8935       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8936         continue;
8937
8938       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8939       boolean is_neighbour = FALSE;
8940
8941       if (level.use_life_bugs)
8942         is_neighbour =
8943           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8944            (IS_FREE(x, y)                             &&  Stop[x][y]));
8945       else
8946         is_neighbour =
8947           (Last[x][y] == element || is_player_cell);
8948
8949       if (is_neighbour)
8950         num_neighbours++;
8951     }
8952
8953     boolean is_free = FALSE;
8954
8955     if (level.use_life_bugs)
8956       is_free = (IS_FREE(xx, yy));
8957     else
8958       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8959
8960     if (xx == ax && yy == ay)           // field in the middle
8961     {
8962       if (num_neighbours < life_parameter[0] ||
8963           num_neighbours > life_parameter[1])
8964       {
8965         Feld[xx][yy] = EL_EMPTY;
8966         if (Feld[xx][yy] != old_element)
8967           TEST_DrawLevelField(xx, yy);
8968         Stop[xx][yy] = TRUE;
8969         changed = TRUE;
8970       }
8971     }
8972     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
8973     {                                   // free border field
8974       if (num_neighbours >= life_parameter[2] &&
8975           num_neighbours <= life_parameter[3])
8976       {
8977         Feld[xx][yy] = element;
8978         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8979         if (Feld[xx][yy] != old_element)
8980           TEST_DrawLevelField(xx, yy);
8981         Stop[xx][yy] = TRUE;
8982         changed = TRUE;
8983       }
8984     }
8985   }
8986
8987   if (changed)
8988     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8989                    SND_GAME_OF_LIFE_GROWING);
8990 }
8991
8992 static void InitRobotWheel(int x, int y)
8993 {
8994   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8995 }
8996
8997 static void RunRobotWheel(int x, int y)
8998 {
8999   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9000 }
9001
9002 static void StopRobotWheel(int x, int y)
9003 {
9004   if (ZX == x && ZY == y)
9005   {
9006     ZX = ZY = -1;
9007
9008     game.robot_wheel_active = FALSE;
9009   }
9010 }
9011
9012 static void InitTimegateWheel(int x, int y)
9013 {
9014   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9015 }
9016
9017 static void RunTimegateWheel(int x, int y)
9018 {
9019   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9020 }
9021
9022 static void InitMagicBallDelay(int x, int y)
9023 {
9024   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9025 }
9026
9027 static void ActivateMagicBall(int bx, int by)
9028 {
9029   int x, y;
9030
9031   if (level.ball_random)
9032   {
9033     int pos_border = RND(8);    // select one of the eight border elements
9034     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9035     int xx = pos_content % 3;
9036     int yy = pos_content / 3;
9037
9038     x = bx - 1 + xx;
9039     y = by - 1 + yy;
9040
9041     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9042       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9043   }
9044   else
9045   {
9046     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9047     {
9048       int xx = x - bx + 1;
9049       int yy = y - by + 1;
9050
9051       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9052         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9053     }
9054   }
9055
9056   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9057 }
9058
9059 static void CheckExit(int x, int y)
9060 {
9061   if (local_player->gems_still_needed > 0 ||
9062       local_player->sokoban_fields_still_needed > 0 ||
9063       local_player->sokoban_objects_still_needed > 0 ||
9064       local_player->lights_still_needed > 0)
9065   {
9066     int element = Feld[x][y];
9067     int graphic = el2img(element);
9068
9069     if (IS_ANIMATED(graphic))
9070       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9071
9072     return;
9073   }
9074
9075   if (AllPlayersGone)   // do not re-open exit door closed after last player
9076     return;
9077
9078   Feld[x][y] = EL_EXIT_OPENING;
9079
9080   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9081 }
9082
9083 static void CheckExitEM(int x, int y)
9084 {
9085   if (local_player->gems_still_needed > 0 ||
9086       local_player->sokoban_fields_still_needed > 0 ||
9087       local_player->sokoban_objects_still_needed > 0 ||
9088       local_player->lights_still_needed > 0)
9089   {
9090     int element = Feld[x][y];
9091     int graphic = el2img(element);
9092
9093     if (IS_ANIMATED(graphic))
9094       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9095
9096     return;
9097   }
9098
9099   if (AllPlayersGone)   // do not re-open exit door closed after last player
9100     return;
9101
9102   Feld[x][y] = EL_EM_EXIT_OPENING;
9103
9104   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9105 }
9106
9107 static void CheckExitSteel(int x, int y)
9108 {
9109   if (local_player->gems_still_needed > 0 ||
9110       local_player->sokoban_fields_still_needed > 0 ||
9111       local_player->sokoban_objects_still_needed > 0 ||
9112       local_player->lights_still_needed > 0)
9113   {
9114     int element = Feld[x][y];
9115     int graphic = el2img(element);
9116
9117     if (IS_ANIMATED(graphic))
9118       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9119
9120     return;
9121   }
9122
9123   if (AllPlayersGone)   // do not re-open exit door closed after last player
9124     return;
9125
9126   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9127
9128   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9129 }
9130
9131 static void CheckExitSteelEM(int x, int y)
9132 {
9133   if (local_player->gems_still_needed > 0 ||
9134       local_player->sokoban_fields_still_needed > 0 ||
9135       local_player->sokoban_objects_still_needed > 0 ||
9136       local_player->lights_still_needed > 0)
9137   {
9138     int element = Feld[x][y];
9139     int graphic = el2img(element);
9140
9141     if (IS_ANIMATED(graphic))
9142       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9143
9144     return;
9145   }
9146
9147   if (AllPlayersGone)   // do not re-open exit door closed after last player
9148     return;
9149
9150   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9151
9152   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9153 }
9154
9155 static void CheckExitSP(int x, int y)
9156 {
9157   if (local_player->gems_still_needed > 0)
9158   {
9159     int element = Feld[x][y];
9160     int graphic = el2img(element);
9161
9162     if (IS_ANIMATED(graphic))
9163       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9164
9165     return;
9166   }
9167
9168   if (AllPlayersGone)   // do not re-open exit door closed after last player
9169     return;
9170
9171   Feld[x][y] = EL_SP_EXIT_OPENING;
9172
9173   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9174 }
9175
9176 static void CloseAllOpenTimegates(void)
9177 {
9178   int x, y;
9179
9180   SCAN_PLAYFIELD(x, y)
9181   {
9182     int element = Feld[x][y];
9183
9184     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9185     {
9186       Feld[x][y] = EL_TIMEGATE_CLOSING;
9187
9188       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9189     }
9190   }
9191 }
9192
9193 static void DrawTwinkleOnField(int x, int y)
9194 {
9195   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9196     return;
9197
9198   if (Feld[x][y] == EL_BD_DIAMOND)
9199     return;
9200
9201   if (MovDelay[x][y] == 0)      // next animation frame
9202     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9203
9204   if (MovDelay[x][y] != 0)      // wait some time before next frame
9205   {
9206     MovDelay[x][y]--;
9207
9208     DrawLevelElementAnimation(x, y, Feld[x][y]);
9209
9210     if (MovDelay[x][y] != 0)
9211     {
9212       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9213                                            10 - MovDelay[x][y]);
9214
9215       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9216     }
9217   }
9218 }
9219
9220 static void MauerWaechst(int x, int y)
9221 {
9222   int delay = 6;
9223
9224   if (!MovDelay[x][y])          // next animation frame
9225     MovDelay[x][y] = 3 * delay;
9226
9227   if (MovDelay[x][y])           // wait some time before next frame
9228   {
9229     MovDelay[x][y]--;
9230
9231     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9232     {
9233       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9234       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9235
9236       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9237     }
9238
9239     if (!MovDelay[x][y])
9240     {
9241       if (MovDir[x][y] == MV_LEFT)
9242       {
9243         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9244           TEST_DrawLevelField(x - 1, y);
9245       }
9246       else if (MovDir[x][y] == MV_RIGHT)
9247       {
9248         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9249           TEST_DrawLevelField(x + 1, y);
9250       }
9251       else if (MovDir[x][y] == MV_UP)
9252       {
9253         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9254           TEST_DrawLevelField(x, y - 1);
9255       }
9256       else
9257       {
9258         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9259           TEST_DrawLevelField(x, y + 1);
9260       }
9261
9262       Feld[x][y] = Store[x][y];
9263       Store[x][y] = 0;
9264       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9265       TEST_DrawLevelField(x, y);
9266     }
9267   }
9268 }
9269
9270 static void MauerAbleger(int ax, int ay)
9271 {
9272   int element = Feld[ax][ay];
9273   int graphic = el2img(element);
9274   boolean oben_frei = FALSE, unten_frei = FALSE;
9275   boolean links_frei = FALSE, rechts_frei = FALSE;
9276   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9277   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9278   boolean new_wall = FALSE;
9279
9280   if (IS_ANIMATED(graphic))
9281     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9282
9283   if (!MovDelay[ax][ay])        // start building new wall
9284     MovDelay[ax][ay] = 6;
9285
9286   if (MovDelay[ax][ay])         // wait some time before building new wall
9287   {
9288     MovDelay[ax][ay]--;
9289     if (MovDelay[ax][ay])
9290       return;
9291   }
9292
9293   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9294     oben_frei = TRUE;
9295   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9296     unten_frei = TRUE;
9297   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9298     links_frei = TRUE;
9299   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9300     rechts_frei = TRUE;
9301
9302   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9303       element == EL_EXPANDABLE_WALL_ANY)
9304   {
9305     if (oben_frei)
9306     {
9307       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9308       Store[ax][ay-1] = element;
9309       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9310       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9311         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9312                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9313       new_wall = TRUE;
9314     }
9315     if (unten_frei)
9316     {
9317       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9318       Store[ax][ay+1] = element;
9319       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9320       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9321         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9322                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9323       new_wall = TRUE;
9324     }
9325   }
9326
9327   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9328       element == EL_EXPANDABLE_WALL_ANY ||
9329       element == EL_EXPANDABLE_WALL ||
9330       element == EL_BD_EXPANDABLE_WALL)
9331   {
9332     if (links_frei)
9333     {
9334       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9335       Store[ax-1][ay] = element;
9336       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9337       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9338         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9339                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9340       new_wall = TRUE;
9341     }
9342
9343     if (rechts_frei)
9344     {
9345       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9346       Store[ax+1][ay] = element;
9347       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9348       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9349         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9350                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9351       new_wall = TRUE;
9352     }
9353   }
9354
9355   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9356     TEST_DrawLevelField(ax, ay);
9357
9358   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9359     oben_massiv = TRUE;
9360   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9361     unten_massiv = TRUE;
9362   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9363     links_massiv = TRUE;
9364   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9365     rechts_massiv = TRUE;
9366
9367   if (((oben_massiv && unten_massiv) ||
9368        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9369        element == EL_EXPANDABLE_WALL) &&
9370       ((links_massiv && rechts_massiv) ||
9371        element == EL_EXPANDABLE_WALL_VERTICAL))
9372     Feld[ax][ay] = EL_WALL;
9373
9374   if (new_wall)
9375     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9376 }
9377
9378 static void MauerAblegerStahl(int ax, int ay)
9379 {
9380   int element = Feld[ax][ay];
9381   int graphic = el2img(element);
9382   boolean oben_frei = FALSE, unten_frei = FALSE;
9383   boolean links_frei = FALSE, rechts_frei = FALSE;
9384   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9385   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9386   boolean new_wall = FALSE;
9387
9388   if (IS_ANIMATED(graphic))
9389     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9390
9391   if (!MovDelay[ax][ay])        // start building new wall
9392     MovDelay[ax][ay] = 6;
9393
9394   if (MovDelay[ax][ay])         // wait some time before building new wall
9395   {
9396     MovDelay[ax][ay]--;
9397     if (MovDelay[ax][ay])
9398       return;
9399   }
9400
9401   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9402     oben_frei = TRUE;
9403   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9404     unten_frei = TRUE;
9405   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9406     links_frei = TRUE;
9407   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9408     rechts_frei = TRUE;
9409
9410   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9411       element == EL_EXPANDABLE_STEELWALL_ANY)
9412   {
9413     if (oben_frei)
9414     {
9415       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9416       Store[ax][ay-1] = element;
9417       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9418       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9419         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9420                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9421       new_wall = TRUE;
9422     }
9423     if (unten_frei)
9424     {
9425       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9426       Store[ax][ay+1] = element;
9427       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9428       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9429         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9430                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9431       new_wall = TRUE;
9432     }
9433   }
9434
9435   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9436       element == EL_EXPANDABLE_STEELWALL_ANY)
9437   {
9438     if (links_frei)
9439     {
9440       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9441       Store[ax-1][ay] = element;
9442       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9443       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9444         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9445                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9446       new_wall = TRUE;
9447     }
9448
9449     if (rechts_frei)
9450     {
9451       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9452       Store[ax+1][ay] = element;
9453       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9454       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9455         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9456                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9457       new_wall = TRUE;
9458     }
9459   }
9460
9461   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9462     oben_massiv = TRUE;
9463   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9464     unten_massiv = TRUE;
9465   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9466     links_massiv = TRUE;
9467   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9468     rechts_massiv = TRUE;
9469
9470   if (((oben_massiv && unten_massiv) ||
9471        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9472       ((links_massiv && rechts_massiv) ||
9473        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9474     Feld[ax][ay] = EL_STEELWALL;
9475
9476   if (new_wall)
9477     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9478 }
9479
9480 static void CheckForDragon(int x, int y)
9481 {
9482   int i, j;
9483   boolean dragon_found = FALSE;
9484   static int xy[4][2] =
9485   {
9486     { 0, -1 },
9487     { -1, 0 },
9488     { +1, 0 },
9489     { 0, +1 }
9490   };
9491
9492   for (i = 0; i < NUM_DIRECTIONS; i++)
9493   {
9494     for (j = 0; j < 4; j++)
9495     {
9496       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9497
9498       if (IN_LEV_FIELD(xx, yy) &&
9499           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9500       {
9501         if (Feld[xx][yy] == EL_DRAGON)
9502           dragon_found = TRUE;
9503       }
9504       else
9505         break;
9506     }
9507   }
9508
9509   if (!dragon_found)
9510   {
9511     for (i = 0; i < NUM_DIRECTIONS; i++)
9512     {
9513       for (j = 0; j < 3; j++)
9514       {
9515         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9516   
9517         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9518         {
9519           Feld[xx][yy] = EL_EMPTY;
9520           TEST_DrawLevelField(xx, yy);
9521         }
9522         else
9523           break;
9524       }
9525     }
9526   }
9527 }
9528
9529 static void InitBuggyBase(int x, int y)
9530 {
9531   int element = Feld[x][y];
9532   int activating_delay = FRAMES_PER_SECOND / 4;
9533
9534   ChangeDelay[x][y] =
9535     (element == EL_SP_BUGGY_BASE ?
9536      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9537      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9538      activating_delay :
9539      element == EL_SP_BUGGY_BASE_ACTIVE ?
9540      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9541 }
9542
9543 static void WarnBuggyBase(int x, int y)
9544 {
9545   int i;
9546   static int xy[4][2] =
9547   {
9548     { 0, -1 },
9549     { -1, 0 },
9550     { +1, 0 },
9551     { 0, +1 }
9552   };
9553
9554   for (i = 0; i < NUM_DIRECTIONS; i++)
9555   {
9556     int xx = x + xy[i][0];
9557     int yy = y + xy[i][1];
9558
9559     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9560     {
9561       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9562
9563       break;
9564     }
9565   }
9566 }
9567
9568 static void InitTrap(int x, int y)
9569 {
9570   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9571 }
9572
9573 static void ActivateTrap(int x, int y)
9574 {
9575   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9576 }
9577
9578 static void ChangeActiveTrap(int x, int y)
9579 {
9580   int graphic = IMG_TRAP_ACTIVE;
9581
9582   // if new animation frame was drawn, correct crumbled sand border
9583   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9584     TEST_DrawLevelFieldCrumbled(x, y);
9585 }
9586
9587 static int getSpecialActionElement(int element, int number, int base_element)
9588 {
9589   return (element != EL_EMPTY ? element :
9590           number != -1 ? base_element + number - 1 :
9591           EL_EMPTY);
9592 }
9593
9594 static int getModifiedActionNumber(int value_old, int operator, int operand,
9595                                    int value_min, int value_max)
9596 {
9597   int value_new = (operator == CA_MODE_SET      ? operand :
9598                    operator == CA_MODE_ADD      ? value_old + operand :
9599                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9600                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9601                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9602                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9603                    value_old);
9604
9605   return (value_new < value_min ? value_min :
9606           value_new > value_max ? value_max :
9607           value_new);
9608 }
9609
9610 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9611 {
9612   struct ElementInfo *ei = &element_info[element];
9613   struct ElementChangeInfo *change = &ei->change_page[page];
9614   int target_element = change->target_element;
9615   int action_type = change->action_type;
9616   int action_mode = change->action_mode;
9617   int action_arg = change->action_arg;
9618   int action_element = change->action_element;
9619   int i;
9620
9621   if (!change->has_action)
9622     return;
9623
9624   // ---------- determine action paramater values -----------------------------
9625
9626   int level_time_value =
9627     (level.time > 0 ? TimeLeft :
9628      TimePlayed);
9629
9630   int action_arg_element_raw =
9631     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9632      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9633      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9634      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9635      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9636      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9637      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9638      EL_EMPTY);
9639   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9640
9641   int action_arg_direction =
9642     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9643      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9644      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9645      change->actual_trigger_side :
9646      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9647      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9648      MV_NONE);
9649
9650   int action_arg_number_min =
9651     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9652      CA_ARG_MIN);
9653
9654   int action_arg_number_max =
9655     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9656      action_type == CA_SET_LEVEL_GEMS ? 999 :
9657      action_type == CA_SET_LEVEL_TIME ? 9999 :
9658      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9659      action_type == CA_SET_CE_VALUE ? 9999 :
9660      action_type == CA_SET_CE_SCORE ? 9999 :
9661      CA_ARG_MAX);
9662
9663   int action_arg_number_reset =
9664     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9665      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9666      action_type == CA_SET_LEVEL_TIME ? level.time :
9667      action_type == CA_SET_LEVEL_SCORE ? 0 :
9668      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9669      action_type == CA_SET_CE_SCORE ? 0 :
9670      0);
9671
9672   int action_arg_number =
9673     (action_arg <= CA_ARG_MAX ? action_arg :
9674      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9675      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9676      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9677      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9678      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9679      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9680      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9681      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9682      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9683      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9684      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9685      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9686      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9687      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9688      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9689      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9690      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9691      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9692      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9693      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9694      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9695      -1);
9696
9697   int action_arg_number_old =
9698     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9699      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9700      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9701      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9702      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9703      0);
9704
9705   int action_arg_number_new =
9706     getModifiedActionNumber(action_arg_number_old,
9707                             action_mode, action_arg_number,
9708                             action_arg_number_min, action_arg_number_max);
9709
9710   int trigger_player_bits =
9711     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9712      change->actual_trigger_player_bits : change->trigger_player);
9713
9714   int action_arg_player_bits =
9715     (action_arg >= CA_ARG_PLAYER_1 &&
9716      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9717      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9718      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9719      PLAYER_BITS_ANY);
9720
9721   // ---------- execute action  -----------------------------------------------
9722
9723   switch (action_type)
9724   {
9725     case CA_NO_ACTION:
9726     {
9727       return;
9728     }
9729
9730     // ---------- level actions  ----------------------------------------------
9731
9732     case CA_RESTART_LEVEL:
9733     {
9734       game.restart_level = TRUE;
9735
9736       break;
9737     }
9738
9739     case CA_SHOW_ENVELOPE:
9740     {
9741       int element = getSpecialActionElement(action_arg_element,
9742                                             action_arg_number, EL_ENVELOPE_1);
9743
9744       if (IS_ENVELOPE(element))
9745         local_player->show_envelope = element;
9746
9747       break;
9748     }
9749
9750     case CA_SET_LEVEL_TIME:
9751     {
9752       if (level.time > 0)       // only modify limited time value
9753       {
9754         TimeLeft = action_arg_number_new;
9755
9756         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9757
9758         DisplayGameControlValues();
9759
9760         if (!TimeLeft && setup.time_limit)
9761           for (i = 0; i < MAX_PLAYERS; i++)
9762             KillPlayer(&stored_player[i]);
9763       }
9764
9765       break;
9766     }
9767
9768     case CA_SET_LEVEL_SCORE:
9769     {
9770       local_player->score = action_arg_number_new;
9771
9772       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9773
9774       DisplayGameControlValues();
9775
9776       break;
9777     }
9778
9779     case CA_SET_LEVEL_GEMS:
9780     {
9781       local_player->gems_still_needed = action_arg_number_new;
9782
9783       game.snapshot.collected_item = TRUE;
9784
9785       game_panel_controls[GAME_PANEL_GEMS].value =
9786         local_player->gems_still_needed;
9787
9788       DisplayGameControlValues();
9789
9790       break;
9791     }
9792
9793     case CA_SET_LEVEL_WIND:
9794     {
9795       game.wind_direction = action_arg_direction;
9796
9797       break;
9798     }
9799
9800     case CA_SET_LEVEL_RANDOM_SEED:
9801     {
9802       // ensure that setting a new random seed while playing is predictable
9803       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9804
9805       break;
9806     }
9807
9808     // ---------- player actions  ---------------------------------------------
9809
9810     case CA_MOVE_PLAYER:
9811     {
9812       // automatically move to the next field in specified direction
9813       for (i = 0; i < MAX_PLAYERS; i++)
9814         if (trigger_player_bits & (1 << i))
9815           stored_player[i].programmed_action = action_arg_direction;
9816
9817       break;
9818     }
9819
9820     case CA_EXIT_PLAYER:
9821     {
9822       for (i = 0; i < MAX_PLAYERS; i++)
9823         if (action_arg_player_bits & (1 << i))
9824           ExitPlayer(&stored_player[i]);
9825
9826       if (AllPlayersGone)
9827         PlayerWins(local_player);
9828
9829       break;
9830     }
9831
9832     case CA_KILL_PLAYER:
9833     {
9834       for (i = 0; i < MAX_PLAYERS; i++)
9835         if (action_arg_player_bits & (1 << i))
9836           KillPlayer(&stored_player[i]);
9837
9838       break;
9839     }
9840
9841     case CA_SET_PLAYER_KEYS:
9842     {
9843       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9844       int element = getSpecialActionElement(action_arg_element,
9845                                             action_arg_number, EL_KEY_1);
9846
9847       if (IS_KEY(element))
9848       {
9849         for (i = 0; i < MAX_PLAYERS; i++)
9850         {
9851           if (trigger_player_bits & (1 << i))
9852           {
9853             stored_player[i].key[KEY_NR(element)] = key_state;
9854
9855             DrawGameDoorValues();
9856           }
9857         }
9858       }
9859
9860       break;
9861     }
9862
9863     case CA_SET_PLAYER_SPEED:
9864     {
9865       for (i = 0; i < MAX_PLAYERS; i++)
9866       {
9867         if (trigger_player_bits & (1 << i))
9868         {
9869           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9870
9871           if (action_arg == CA_ARG_SPEED_FASTER &&
9872               stored_player[i].cannot_move)
9873           {
9874             action_arg_number = STEPSIZE_VERY_SLOW;
9875           }
9876           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9877                    action_arg == CA_ARG_SPEED_FASTER)
9878           {
9879             action_arg_number = 2;
9880             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9881                            CA_MODE_MULTIPLY);
9882           }
9883           else if (action_arg == CA_ARG_NUMBER_RESET)
9884           {
9885             action_arg_number = level.initial_player_stepsize[i];
9886           }
9887
9888           move_stepsize =
9889             getModifiedActionNumber(move_stepsize,
9890                                     action_mode,
9891                                     action_arg_number,
9892                                     action_arg_number_min,
9893                                     action_arg_number_max);
9894
9895           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9896         }
9897       }
9898
9899       break;
9900     }
9901
9902     case CA_SET_PLAYER_SHIELD:
9903     {
9904       for (i = 0; i < MAX_PLAYERS; i++)
9905       {
9906         if (trigger_player_bits & (1 << i))
9907         {
9908           if (action_arg == CA_ARG_SHIELD_OFF)
9909           {
9910             stored_player[i].shield_normal_time_left = 0;
9911             stored_player[i].shield_deadly_time_left = 0;
9912           }
9913           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9914           {
9915             stored_player[i].shield_normal_time_left = 999999;
9916           }
9917           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9918           {
9919             stored_player[i].shield_normal_time_left = 999999;
9920             stored_player[i].shield_deadly_time_left = 999999;
9921           }
9922         }
9923       }
9924
9925       break;
9926     }
9927
9928     case CA_SET_PLAYER_GRAVITY:
9929     {
9930       for (i = 0; i < MAX_PLAYERS; i++)
9931       {
9932         if (trigger_player_bits & (1 << i))
9933         {
9934           stored_player[i].gravity =
9935             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9936              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9937              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9938              stored_player[i].gravity);
9939         }
9940       }
9941
9942       break;
9943     }
9944
9945     case CA_SET_PLAYER_ARTWORK:
9946     {
9947       for (i = 0; i < MAX_PLAYERS; i++)
9948       {
9949         if (trigger_player_bits & (1 << i))
9950         {
9951           int artwork_element = action_arg_element;
9952
9953           if (action_arg == CA_ARG_ELEMENT_RESET)
9954             artwork_element =
9955               (level.use_artwork_element[i] ? level.artwork_element[i] :
9956                stored_player[i].element_nr);
9957
9958           if (stored_player[i].artwork_element != artwork_element)
9959             stored_player[i].Frame = 0;
9960
9961           stored_player[i].artwork_element = artwork_element;
9962
9963           SetPlayerWaiting(&stored_player[i], FALSE);
9964
9965           // set number of special actions for bored and sleeping animation
9966           stored_player[i].num_special_action_bored =
9967             get_num_special_action(artwork_element,
9968                                    ACTION_BORING_1, ACTION_BORING_LAST);
9969           stored_player[i].num_special_action_sleeping =
9970             get_num_special_action(artwork_element,
9971                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9972         }
9973       }
9974
9975       break;
9976     }
9977
9978     case CA_SET_PLAYER_INVENTORY:
9979     {
9980       for (i = 0; i < MAX_PLAYERS; i++)
9981       {
9982         struct PlayerInfo *player = &stored_player[i];
9983         int j, k;
9984
9985         if (trigger_player_bits & (1 << i))
9986         {
9987           int inventory_element = action_arg_element;
9988
9989           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9990               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9991               action_arg == CA_ARG_ELEMENT_ACTION)
9992           {
9993             int element = inventory_element;
9994             int collect_count = element_info[element].collect_count_initial;
9995
9996             if (!IS_CUSTOM_ELEMENT(element))
9997               collect_count = 1;
9998
9999             if (collect_count == 0)
10000               player->inventory_infinite_element = element;
10001             else
10002               for (k = 0; k < collect_count; k++)
10003                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10004                   player->inventory_element[player->inventory_size++] =
10005                     element;
10006           }
10007           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10008                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10009                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10010           {
10011             if (player->inventory_infinite_element != EL_UNDEFINED &&
10012                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10013                                      action_arg_element_raw))
10014               player->inventory_infinite_element = EL_UNDEFINED;
10015
10016             for (k = 0, j = 0; j < player->inventory_size; j++)
10017             {
10018               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10019                                         action_arg_element_raw))
10020                 player->inventory_element[k++] = player->inventory_element[j];
10021             }
10022
10023             player->inventory_size = k;
10024           }
10025           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10026           {
10027             if (player->inventory_size > 0)
10028             {
10029               for (j = 0; j < player->inventory_size - 1; j++)
10030                 player->inventory_element[j] = player->inventory_element[j + 1];
10031
10032               player->inventory_size--;
10033             }
10034           }
10035           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10036           {
10037             if (player->inventory_size > 0)
10038               player->inventory_size--;
10039           }
10040           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10041           {
10042             player->inventory_infinite_element = EL_UNDEFINED;
10043             player->inventory_size = 0;
10044           }
10045           else if (action_arg == CA_ARG_INVENTORY_RESET)
10046           {
10047             player->inventory_infinite_element = EL_UNDEFINED;
10048             player->inventory_size = 0;
10049
10050             if (level.use_initial_inventory[i])
10051             {
10052               for (j = 0; j < level.initial_inventory_size[i]; j++)
10053               {
10054                 int element = level.initial_inventory_content[i][j];
10055                 int collect_count = element_info[element].collect_count_initial;
10056
10057                 if (!IS_CUSTOM_ELEMENT(element))
10058                   collect_count = 1;
10059
10060                 if (collect_count == 0)
10061                   player->inventory_infinite_element = element;
10062                 else
10063                   for (k = 0; k < collect_count; k++)
10064                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10065                       player->inventory_element[player->inventory_size++] =
10066                         element;
10067               }
10068             }
10069           }
10070         }
10071       }
10072
10073       break;
10074     }
10075
10076     // ---------- CE actions  -------------------------------------------------
10077
10078     case CA_SET_CE_VALUE:
10079     {
10080       int last_ce_value = CustomValue[x][y];
10081
10082       CustomValue[x][y] = action_arg_number_new;
10083
10084       if (CustomValue[x][y] != last_ce_value)
10085       {
10086         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10087         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10088
10089         if (CustomValue[x][y] == 0)
10090         {
10091           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10092           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10093         }
10094       }
10095
10096       break;
10097     }
10098
10099     case CA_SET_CE_SCORE:
10100     {
10101       int last_ce_score = ei->collect_score;
10102
10103       ei->collect_score = action_arg_number_new;
10104
10105       if (ei->collect_score != last_ce_score)
10106       {
10107         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10108         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10109
10110         if (ei->collect_score == 0)
10111         {
10112           int xx, yy;
10113
10114           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10115           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10116
10117           /*
10118             This is a very special case that seems to be a mixture between
10119             CheckElementChange() and CheckTriggeredElementChange(): while
10120             the first one only affects single elements that are triggered
10121             directly, the second one affects multiple elements in the playfield
10122             that are triggered indirectly by another element. This is a third
10123             case: Changing the CE score always affects multiple identical CEs,
10124             so every affected CE must be checked, not only the single CE for
10125             which the CE score was changed in the first place (as every instance
10126             of that CE shares the same CE score, and therefore also can change)!
10127           */
10128           SCAN_PLAYFIELD(xx, yy)
10129           {
10130             if (Feld[xx][yy] == element)
10131               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10132                                  CE_SCORE_GETS_ZERO);
10133           }
10134         }
10135       }
10136
10137       break;
10138     }
10139
10140     case CA_SET_CE_ARTWORK:
10141     {
10142       int artwork_element = action_arg_element;
10143       boolean reset_frame = FALSE;
10144       int xx, yy;
10145
10146       if (action_arg == CA_ARG_ELEMENT_RESET)
10147         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10148                            element);
10149
10150       if (ei->gfx_element != artwork_element)
10151         reset_frame = TRUE;
10152
10153       ei->gfx_element = artwork_element;
10154
10155       SCAN_PLAYFIELD(xx, yy)
10156       {
10157         if (Feld[xx][yy] == element)
10158         {
10159           if (reset_frame)
10160           {
10161             ResetGfxAnimation(xx, yy);
10162             ResetRandomAnimationValue(xx, yy);
10163           }
10164
10165           TEST_DrawLevelField(xx, yy);
10166         }
10167       }
10168
10169       break;
10170     }
10171
10172     // ---------- engine actions  ---------------------------------------------
10173
10174     case CA_SET_ENGINE_SCAN_MODE:
10175     {
10176       InitPlayfieldScanMode(action_arg);
10177
10178       break;
10179     }
10180
10181     default:
10182       break;
10183   }
10184 }
10185
10186 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10187 {
10188   int old_element = Feld[x][y];
10189   int new_element = GetElementFromGroupElement(element);
10190   int previous_move_direction = MovDir[x][y];
10191   int last_ce_value = CustomValue[x][y];
10192   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10193   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10194   boolean add_player_onto_element = (new_element_is_player &&
10195                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10196                                      IS_WALKABLE(old_element));
10197
10198   if (!add_player_onto_element)
10199   {
10200     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10201       RemoveMovingField(x, y);
10202     else
10203       RemoveField(x, y);
10204
10205     Feld[x][y] = new_element;
10206
10207     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10208       MovDir[x][y] = previous_move_direction;
10209
10210     if (element_info[new_element].use_last_ce_value)
10211       CustomValue[x][y] = last_ce_value;
10212
10213     InitField_WithBug1(x, y, FALSE);
10214
10215     new_element = Feld[x][y];   // element may have changed
10216
10217     ResetGfxAnimation(x, y);
10218     ResetRandomAnimationValue(x, y);
10219
10220     TEST_DrawLevelField(x, y);
10221
10222     if (GFX_CRUMBLED(new_element))
10223       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10224   }
10225
10226   // check if element under the player changes from accessible to unaccessible
10227   // (needed for special case of dropping element which then changes)
10228   // (must be checked after creating new element for walkable group elements)
10229   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10230       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10231   {
10232     Bang(x, y);
10233
10234     return;
10235   }
10236
10237   // "ChangeCount" not set yet to allow "entered by player" change one time
10238   if (new_element_is_player)
10239     RelocatePlayer(x, y, new_element);
10240
10241   if (is_change)
10242     ChangeCount[x][y]++;        // count number of changes in the same frame
10243
10244   TestIfBadThingTouchesPlayer(x, y);
10245   TestIfPlayerTouchesCustomElement(x, y);
10246   TestIfElementTouchesCustomElement(x, y);
10247 }
10248
10249 static void CreateField(int x, int y, int element)
10250 {
10251   CreateFieldExt(x, y, element, FALSE);
10252 }
10253
10254 static void CreateElementFromChange(int x, int y, int element)
10255 {
10256   element = GET_VALID_RUNTIME_ELEMENT(element);
10257
10258   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10259   {
10260     int old_element = Feld[x][y];
10261
10262     // prevent changed element from moving in same engine frame
10263     // unless both old and new element can either fall or move
10264     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10265         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10266       Stop[x][y] = TRUE;
10267   }
10268
10269   CreateFieldExt(x, y, element, TRUE);
10270 }
10271
10272 static boolean ChangeElement(int x, int y, int element, int page)
10273 {
10274   struct ElementInfo *ei = &element_info[element];
10275   struct ElementChangeInfo *change = &ei->change_page[page];
10276   int ce_value = CustomValue[x][y];
10277   int ce_score = ei->collect_score;
10278   int target_element;
10279   int old_element = Feld[x][y];
10280
10281   // always use default change event to prevent running into a loop
10282   if (ChangeEvent[x][y] == -1)
10283     ChangeEvent[x][y] = CE_DELAY;
10284
10285   if (ChangeEvent[x][y] == CE_DELAY)
10286   {
10287     // reset actual trigger element, trigger player and action element
10288     change->actual_trigger_element = EL_EMPTY;
10289     change->actual_trigger_player = EL_EMPTY;
10290     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10291     change->actual_trigger_side = CH_SIDE_NONE;
10292     change->actual_trigger_ce_value = 0;
10293     change->actual_trigger_ce_score = 0;
10294   }
10295
10296   // do not change elements more than a specified maximum number of changes
10297   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10298     return FALSE;
10299
10300   ChangeCount[x][y]++;          // count number of changes in the same frame
10301
10302   if (change->explode)
10303   {
10304     Bang(x, y);
10305
10306     return TRUE;
10307   }
10308
10309   if (change->use_target_content)
10310   {
10311     boolean complete_replace = TRUE;
10312     boolean can_replace[3][3];
10313     int xx, yy;
10314
10315     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10316     {
10317       boolean is_empty;
10318       boolean is_walkable;
10319       boolean is_diggable;
10320       boolean is_collectible;
10321       boolean is_removable;
10322       boolean is_destructible;
10323       int ex = x + xx - 1;
10324       int ey = y + yy - 1;
10325       int content_element = change->target_content.e[xx][yy];
10326       int e;
10327
10328       can_replace[xx][yy] = TRUE;
10329
10330       if (ex == x && ey == y)   // do not check changing element itself
10331         continue;
10332
10333       if (content_element == EL_EMPTY_SPACE)
10334       {
10335         can_replace[xx][yy] = FALSE;    // do not replace border with space
10336
10337         continue;
10338       }
10339
10340       if (!IN_LEV_FIELD(ex, ey))
10341       {
10342         can_replace[xx][yy] = FALSE;
10343         complete_replace = FALSE;
10344
10345         continue;
10346       }
10347
10348       e = Feld[ex][ey];
10349
10350       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10351         e = MovingOrBlocked2Element(ex, ey);
10352
10353       is_empty = (IS_FREE(ex, ey) ||
10354                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10355
10356       is_walkable     = (is_empty || IS_WALKABLE(e));
10357       is_diggable     = (is_empty || IS_DIGGABLE(e));
10358       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10359       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10360       is_removable    = (is_diggable || is_collectible);
10361
10362       can_replace[xx][yy] =
10363         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10364           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10365           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10366           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10367           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10368           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10369          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10370
10371       if (!can_replace[xx][yy])
10372         complete_replace = FALSE;
10373     }
10374
10375     if (!change->only_if_complete || complete_replace)
10376     {
10377       boolean something_has_changed = FALSE;
10378
10379       if (change->only_if_complete && change->use_random_replace &&
10380           RND(100) < change->random_percentage)
10381         return FALSE;
10382
10383       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10384       {
10385         int ex = x + xx - 1;
10386         int ey = y + yy - 1;
10387         int content_element;
10388
10389         if (can_replace[xx][yy] && (!change->use_random_replace ||
10390                                     RND(100) < change->random_percentage))
10391         {
10392           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10393             RemoveMovingField(ex, ey);
10394
10395           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10396
10397           content_element = change->target_content.e[xx][yy];
10398           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10399                                               ce_value, ce_score);
10400
10401           CreateElementFromChange(ex, ey, target_element);
10402
10403           something_has_changed = TRUE;
10404
10405           // for symmetry reasons, freeze newly created border elements
10406           if (ex != x || ey != y)
10407             Stop[ex][ey] = TRUE;        // no more moving in this frame
10408         }
10409       }
10410
10411       if (something_has_changed)
10412       {
10413         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10414         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10415       }
10416     }
10417   }
10418   else
10419   {
10420     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10421                                         ce_value, ce_score);
10422
10423     if (element == EL_DIAGONAL_GROWING ||
10424         element == EL_DIAGONAL_SHRINKING)
10425     {
10426       target_element = Store[x][y];
10427
10428       Store[x][y] = EL_EMPTY;
10429     }
10430
10431     CreateElementFromChange(x, y, target_element);
10432
10433     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10434     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10435   }
10436
10437   // this uses direct change before indirect change
10438   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10439
10440   return TRUE;
10441 }
10442
10443 static void HandleElementChange(int x, int y, int page)
10444 {
10445   int element = MovingOrBlocked2Element(x, y);
10446   struct ElementInfo *ei = &element_info[element];
10447   struct ElementChangeInfo *change = &ei->change_page[page];
10448   boolean handle_action_before_change = FALSE;
10449
10450 #ifdef DEBUG
10451   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10452       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10453   {
10454     printf("\n\n");
10455     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10456            x, y, element, element_info[element].token_name);
10457     printf("HandleElementChange(): This should never happen!\n");
10458     printf("\n\n");
10459   }
10460 #endif
10461
10462   // this can happen with classic bombs on walkable, changing elements
10463   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10464   {
10465     return;
10466   }
10467
10468   if (ChangeDelay[x][y] == 0)           // initialize element change
10469   {
10470     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10471
10472     if (change->can_change)
10473     {
10474       // !!! not clear why graphic animation should be reset at all here !!!
10475       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10476       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10477
10478       /*
10479         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10480
10481         When using an animation frame delay of 1 (this only happens with
10482         "sp_zonk.moving.left/right" in the classic graphics), the default
10483         (non-moving) animation shows wrong animation frames (while the
10484         moving animation, like "sp_zonk.moving.left/right", is correct,
10485         so this graphical bug never shows up with the classic graphics).
10486         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10487         be drawn instead of the correct frames 0,1,2,3. This is caused by
10488         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10489         an element change: First when the change delay ("ChangeDelay[][]")
10490         counter has reached zero after decrementing, then a second time in
10491         the next frame (after "GfxFrame[][]" was already incremented) when
10492         "ChangeDelay[][]" is reset to the initial delay value again.
10493
10494         This causes frame 0 to be drawn twice, while the last frame won't
10495         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10496
10497         As some animations may already be cleverly designed around this bug
10498         (at least the "Snake Bite" snake tail animation does this), it cannot
10499         simply be fixed here without breaking such existing animations.
10500         Unfortunately, it cannot easily be detected if a graphics set was
10501         designed "before" or "after" the bug was fixed. As a workaround,
10502         a new graphics set option "game.graphics_engine_version" was added
10503         to be able to specify the game's major release version for which the
10504         graphics set was designed, which can then be used to decide if the
10505         bugfix should be used (version 4 and above) or not (version 3 or
10506         below, or if no version was specified at all, as with old sets).
10507
10508         (The wrong/fixed animation frames can be tested with the test level set
10509         "test_gfxframe" and level "000", which contains a specially prepared
10510         custom element at level position (x/y) == (11/9) which uses the zonk
10511         animation mentioned above. Using "game.graphics_engine_version: 4"
10512         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10513         This can also be seen from the debug output for this test element.)
10514       */
10515
10516       // when a custom element is about to change (for example by change delay),
10517       // do not reset graphic animation when the custom element is moving
10518       if (game.graphics_engine_version < 4 &&
10519           !IS_MOVING(x, y))
10520       {
10521         ResetGfxAnimation(x, y);
10522         ResetRandomAnimationValue(x, y);
10523       }
10524
10525       if (change->pre_change_function)
10526         change->pre_change_function(x, y);
10527     }
10528   }
10529
10530   ChangeDelay[x][y]--;
10531
10532   if (ChangeDelay[x][y] != 0)           // continue element change
10533   {
10534     if (change->can_change)
10535     {
10536       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10537
10538       if (IS_ANIMATED(graphic))
10539         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10540
10541       if (change->change_function)
10542         change->change_function(x, y);
10543     }
10544   }
10545   else                                  // finish element change
10546   {
10547     if (ChangePage[x][y] != -1)         // remember page from delayed change
10548     {
10549       page = ChangePage[x][y];
10550       ChangePage[x][y] = -1;
10551
10552       change = &ei->change_page[page];
10553     }
10554
10555     if (IS_MOVING(x, y))                // never change a running system ;-)
10556     {
10557       ChangeDelay[x][y] = 1;            // try change after next move step
10558       ChangePage[x][y] = page;          // remember page to use for change
10559
10560       return;
10561     }
10562
10563     // special case: set new level random seed before changing element
10564     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10565       handle_action_before_change = TRUE;
10566
10567     if (change->has_action && handle_action_before_change)
10568       ExecuteCustomElementAction(x, y, element, page);
10569
10570     if (change->can_change)
10571     {
10572       if (ChangeElement(x, y, element, page))
10573       {
10574         if (change->post_change_function)
10575           change->post_change_function(x, y);
10576       }
10577     }
10578
10579     if (change->has_action && !handle_action_before_change)
10580       ExecuteCustomElementAction(x, y, element, page);
10581   }
10582 }
10583
10584 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10585                                               int trigger_element,
10586                                               int trigger_event,
10587                                               int trigger_player,
10588                                               int trigger_side,
10589                                               int trigger_page)
10590 {
10591   boolean change_done_any = FALSE;
10592   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10593   int i;
10594
10595   if (!(trigger_events[trigger_element][trigger_event]))
10596     return FALSE;
10597
10598   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10599
10600   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10601   {
10602     int element = EL_CUSTOM_START + i;
10603     boolean change_done = FALSE;
10604     int p;
10605
10606     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10607         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10608       continue;
10609
10610     for (p = 0; p < element_info[element].num_change_pages; p++)
10611     {
10612       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10613
10614       if (change->can_change_or_has_action &&
10615           change->has_event[trigger_event] &&
10616           change->trigger_side & trigger_side &&
10617           change->trigger_player & trigger_player &&
10618           change->trigger_page & trigger_page_bits &&
10619           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10620       {
10621         change->actual_trigger_element = trigger_element;
10622         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10623         change->actual_trigger_player_bits = trigger_player;
10624         change->actual_trigger_side = trigger_side;
10625         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10626         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10627
10628         if ((change->can_change && !change_done) || change->has_action)
10629         {
10630           int x, y;
10631
10632           SCAN_PLAYFIELD(x, y)
10633           {
10634             if (Feld[x][y] == element)
10635             {
10636               if (change->can_change && !change_done)
10637               {
10638                 // if element already changed in this frame, not only prevent
10639                 // another element change (checked in ChangeElement()), but
10640                 // also prevent additional element actions for this element
10641
10642                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10643                     !level.use_action_after_change_bug)
10644                   continue;
10645
10646                 ChangeDelay[x][y] = 1;
10647                 ChangeEvent[x][y] = trigger_event;
10648
10649                 HandleElementChange(x, y, p);
10650               }
10651               else if (change->has_action)
10652               {
10653                 // if element already changed in this frame, not only prevent
10654                 // another element change (checked in ChangeElement()), but
10655                 // also prevent additional element actions for this element
10656
10657                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10658                     !level.use_action_after_change_bug)
10659                   continue;
10660
10661                 ExecuteCustomElementAction(x, y, element, p);
10662                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10663               }
10664             }
10665           }
10666
10667           if (change->can_change)
10668           {
10669             change_done = TRUE;
10670             change_done_any = TRUE;
10671           }
10672         }
10673       }
10674     }
10675   }
10676
10677   RECURSION_LOOP_DETECTION_END();
10678
10679   return change_done_any;
10680 }
10681
10682 static boolean CheckElementChangeExt(int x, int y,
10683                                      int element,
10684                                      int trigger_element,
10685                                      int trigger_event,
10686                                      int trigger_player,
10687                                      int trigger_side)
10688 {
10689   boolean change_done = FALSE;
10690   int p;
10691
10692   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10693       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10694     return FALSE;
10695
10696   if (Feld[x][y] == EL_BLOCKED)
10697   {
10698     Blocked2Moving(x, y, &x, &y);
10699     element = Feld[x][y];
10700   }
10701
10702   // check if element has already changed or is about to change after moving
10703   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10704        Feld[x][y] != element) ||
10705
10706       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10707        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10708         ChangePage[x][y] != -1)))
10709     return FALSE;
10710
10711   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10712
10713   for (p = 0; p < element_info[element].num_change_pages; p++)
10714   {
10715     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10716
10717     /* check trigger element for all events where the element that is checked
10718        for changing interacts with a directly adjacent element -- this is
10719        different to element changes that affect other elements to change on the
10720        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10721     boolean check_trigger_element =
10722       (trigger_event == CE_TOUCHING_X ||
10723        trigger_event == CE_HITTING_X ||
10724        trigger_event == CE_HIT_BY_X ||
10725        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10726
10727     if (change->can_change_or_has_action &&
10728         change->has_event[trigger_event] &&
10729         change->trigger_side & trigger_side &&
10730         change->trigger_player & trigger_player &&
10731         (!check_trigger_element ||
10732          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10733     {
10734       change->actual_trigger_element = trigger_element;
10735       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10736       change->actual_trigger_player_bits = trigger_player;
10737       change->actual_trigger_side = trigger_side;
10738       change->actual_trigger_ce_value = CustomValue[x][y];
10739       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10740
10741       // special case: trigger element not at (x,y) position for some events
10742       if (check_trigger_element)
10743       {
10744         static struct
10745         {
10746           int dx, dy;
10747         } move_xy[] =
10748           {
10749             {  0,  0 },
10750             { -1,  0 },
10751             { +1,  0 },
10752             {  0,  0 },
10753             {  0, -1 },
10754             {  0,  0 }, { 0, 0 }, { 0, 0 },
10755             {  0, +1 }
10756           };
10757
10758         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10759         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10760
10761         change->actual_trigger_ce_value = CustomValue[xx][yy];
10762         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10763       }
10764
10765       if (change->can_change && !change_done)
10766       {
10767         ChangeDelay[x][y] = 1;
10768         ChangeEvent[x][y] = trigger_event;
10769
10770         HandleElementChange(x, y, p);
10771
10772         change_done = TRUE;
10773       }
10774       else if (change->has_action)
10775       {
10776         ExecuteCustomElementAction(x, y, element, p);
10777         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10778       }
10779     }
10780   }
10781
10782   RECURSION_LOOP_DETECTION_END();
10783
10784   return change_done;
10785 }
10786
10787 static void PlayPlayerSound(struct PlayerInfo *player)
10788 {
10789   int jx = player->jx, jy = player->jy;
10790   int sound_element = player->artwork_element;
10791   int last_action = player->last_action_waiting;
10792   int action = player->action_waiting;
10793
10794   if (player->is_waiting)
10795   {
10796     if (action != last_action)
10797       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10798     else
10799       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10800   }
10801   else
10802   {
10803     if (action != last_action)
10804       StopSound(element_info[sound_element].sound[last_action]);
10805
10806     if (last_action == ACTION_SLEEPING)
10807       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10808   }
10809 }
10810
10811 static void PlayAllPlayersSound(void)
10812 {
10813   int i;
10814
10815   for (i = 0; i < MAX_PLAYERS; i++)
10816     if (stored_player[i].active)
10817       PlayPlayerSound(&stored_player[i]);
10818 }
10819
10820 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10821 {
10822   boolean last_waiting = player->is_waiting;
10823   int move_dir = player->MovDir;
10824
10825   player->dir_waiting = move_dir;
10826   player->last_action_waiting = player->action_waiting;
10827
10828   if (is_waiting)
10829   {
10830     if (!last_waiting)          // not waiting -> waiting
10831     {
10832       player->is_waiting = TRUE;
10833
10834       player->frame_counter_bored =
10835         FrameCounter +
10836         game.player_boring_delay_fixed +
10837         GetSimpleRandom(game.player_boring_delay_random);
10838       player->frame_counter_sleeping =
10839         FrameCounter +
10840         game.player_sleeping_delay_fixed +
10841         GetSimpleRandom(game.player_sleeping_delay_random);
10842
10843       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10844     }
10845
10846     if (game.player_sleeping_delay_fixed +
10847         game.player_sleeping_delay_random > 0 &&
10848         player->anim_delay_counter == 0 &&
10849         player->post_delay_counter == 0 &&
10850         FrameCounter >= player->frame_counter_sleeping)
10851       player->is_sleeping = TRUE;
10852     else if (game.player_boring_delay_fixed +
10853              game.player_boring_delay_random > 0 &&
10854              FrameCounter >= player->frame_counter_bored)
10855       player->is_bored = TRUE;
10856
10857     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10858                               player->is_bored ? ACTION_BORING :
10859                               ACTION_WAITING);
10860
10861     if (player->is_sleeping && player->use_murphy)
10862     {
10863       // special case for sleeping Murphy when leaning against non-free tile
10864
10865       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10866           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10867            !IS_MOVING(player->jx - 1, player->jy)))
10868         move_dir = MV_LEFT;
10869       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10870                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10871                 !IS_MOVING(player->jx + 1, player->jy)))
10872         move_dir = MV_RIGHT;
10873       else
10874         player->is_sleeping = FALSE;
10875
10876       player->dir_waiting = move_dir;
10877     }
10878
10879     if (player->is_sleeping)
10880     {
10881       if (player->num_special_action_sleeping > 0)
10882       {
10883         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10884         {
10885           int last_special_action = player->special_action_sleeping;
10886           int num_special_action = player->num_special_action_sleeping;
10887           int special_action =
10888             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10889              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10890              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10891              last_special_action + 1 : ACTION_SLEEPING);
10892           int special_graphic =
10893             el_act_dir2img(player->artwork_element, special_action, move_dir);
10894
10895           player->anim_delay_counter =
10896             graphic_info[special_graphic].anim_delay_fixed +
10897             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10898           player->post_delay_counter =
10899             graphic_info[special_graphic].post_delay_fixed +
10900             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10901
10902           player->special_action_sleeping = special_action;
10903         }
10904
10905         if (player->anim_delay_counter > 0)
10906         {
10907           player->action_waiting = player->special_action_sleeping;
10908           player->anim_delay_counter--;
10909         }
10910         else if (player->post_delay_counter > 0)
10911         {
10912           player->post_delay_counter--;
10913         }
10914       }
10915     }
10916     else if (player->is_bored)
10917     {
10918       if (player->num_special_action_bored > 0)
10919       {
10920         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10921         {
10922           int special_action =
10923             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10924           int special_graphic =
10925             el_act_dir2img(player->artwork_element, special_action, move_dir);
10926
10927           player->anim_delay_counter =
10928             graphic_info[special_graphic].anim_delay_fixed +
10929             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10930           player->post_delay_counter =
10931             graphic_info[special_graphic].post_delay_fixed +
10932             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10933
10934           player->special_action_bored = special_action;
10935         }
10936
10937         if (player->anim_delay_counter > 0)
10938         {
10939           player->action_waiting = player->special_action_bored;
10940           player->anim_delay_counter--;
10941         }
10942         else if (player->post_delay_counter > 0)
10943         {
10944           player->post_delay_counter--;
10945         }
10946       }
10947     }
10948   }
10949   else if (last_waiting)        // waiting -> not waiting
10950   {
10951     player->is_waiting = FALSE;
10952     player->is_bored = FALSE;
10953     player->is_sleeping = FALSE;
10954
10955     player->frame_counter_bored = -1;
10956     player->frame_counter_sleeping = -1;
10957
10958     player->anim_delay_counter = 0;
10959     player->post_delay_counter = 0;
10960
10961     player->dir_waiting = player->MovDir;
10962     player->action_waiting = ACTION_DEFAULT;
10963
10964     player->special_action_bored = ACTION_DEFAULT;
10965     player->special_action_sleeping = ACTION_DEFAULT;
10966   }
10967 }
10968
10969 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10970 {
10971   if ((!player->is_moving  && player->was_moving) ||
10972       (player->MovPos == 0 && player->was_moving) ||
10973       (player->is_snapping && !player->was_snapping) ||
10974       (player->is_dropping && !player->was_dropping))
10975   {
10976     if (!CheckSaveEngineSnapshotToList())
10977       return;
10978
10979     player->was_moving = FALSE;
10980     player->was_snapping = TRUE;
10981     player->was_dropping = TRUE;
10982   }
10983   else
10984   {
10985     if (player->is_moving)
10986       player->was_moving = TRUE;
10987
10988     if (!player->is_snapping)
10989       player->was_snapping = FALSE;
10990
10991     if (!player->is_dropping)
10992       player->was_dropping = FALSE;
10993   }
10994 }
10995
10996 static void CheckSingleStepMode(struct PlayerInfo *player)
10997 {
10998   if (tape.single_step && tape.recording && !tape.pausing)
10999   {
11000     /* as it is called "single step mode", just return to pause mode when the
11001        player stopped moving after one tile (or never starts moving at all) */
11002     if (!player->is_moving &&
11003         !player->is_pushing &&
11004         !player->is_dropping_pressed)
11005     {
11006       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11007       SnapField(player, 0, 0);                  // stop snapping
11008     }
11009   }
11010
11011   CheckSaveEngineSnapshot(player);
11012 }
11013
11014 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11015 {
11016   int left      = player_action & JOY_LEFT;
11017   int right     = player_action & JOY_RIGHT;
11018   int up        = player_action & JOY_UP;
11019   int down      = player_action & JOY_DOWN;
11020   int button1   = player_action & JOY_BUTTON_1;
11021   int button2   = player_action & JOY_BUTTON_2;
11022   int dx        = (left ? -1 : right ? 1 : 0);
11023   int dy        = (up   ? -1 : down  ? 1 : 0);
11024
11025   if (!player->active || tape.pausing)
11026     return 0;
11027
11028   if (player_action)
11029   {
11030     if (button1)
11031       SnapField(player, dx, dy);
11032     else
11033     {
11034       if (button2)
11035         DropElement(player);
11036
11037       MovePlayer(player, dx, dy);
11038     }
11039
11040     CheckSingleStepMode(player);
11041
11042     SetPlayerWaiting(player, FALSE);
11043
11044     return player_action;
11045   }
11046   else
11047   {
11048     // no actions for this player (no input at player's configured device)
11049
11050     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11051     SnapField(player, 0, 0);
11052     CheckGravityMovementWhenNotMoving(player);
11053
11054     if (player->MovPos == 0)
11055       SetPlayerWaiting(player, TRUE);
11056
11057     if (player->MovPos == 0)    // needed for tape.playing
11058       player->is_moving = FALSE;
11059
11060     player->is_dropping = FALSE;
11061     player->is_dropping_pressed = FALSE;
11062     player->drop_pressed_delay = 0;
11063
11064     CheckSingleStepMode(player);
11065
11066     return 0;
11067   }
11068 }
11069
11070 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11071                                          byte *tape_action)
11072 {
11073   if (!tape.use_mouse)
11074     return;
11075
11076   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11077   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11078   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11079 }
11080
11081 static void SetTapeActionFromMouseAction(byte *tape_action,
11082                                          struct MouseActionInfo *mouse_action)
11083 {
11084   if (!tape.use_mouse)
11085     return;
11086
11087   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11088   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11089   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11090 }
11091
11092 static void CheckLevelSolved(void)
11093 {
11094   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11095   {
11096     if (level.native_em_level->lev->home == 0)  // all players at home
11097     {
11098       PlayerWins(local_player);
11099
11100       AllPlayersGone = TRUE;
11101
11102       level.native_em_level->lev->home = -1;
11103     }
11104
11105     if (level.native_em_level->ply[0]->alive == 0 &&
11106         level.native_em_level->ply[1]->alive == 0 &&
11107         level.native_em_level->ply[2]->alive == 0 &&
11108         level.native_em_level->ply[3]->alive == 0)      // all dead
11109       AllPlayersGone = TRUE;
11110   }
11111   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11112   {
11113     if (game_sp.LevelSolved &&
11114         !game_sp.GameOver)                              // game won
11115     {
11116       PlayerWins(local_player);
11117
11118       game_sp.GameOver = TRUE;
11119
11120       AllPlayersGone = TRUE;
11121     }
11122
11123     if (game_sp.GameOver)                               // game lost
11124       AllPlayersGone = TRUE;
11125   }
11126   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11127   {
11128     if (game_mm.level_solved &&
11129         !game_mm.game_over)                             // game won
11130     {
11131       PlayerWins(local_player);
11132
11133       game_mm.game_over = TRUE;
11134
11135       AllPlayersGone = TRUE;
11136     }
11137
11138     if (game_mm.game_over)                              // game lost
11139       AllPlayersGone = TRUE;
11140   }
11141 }
11142
11143 static void CheckLevelTime(void)
11144 {
11145   int i;
11146
11147   if (TimeFrames >= FRAMES_PER_SECOND)
11148   {
11149     TimeFrames = 0;
11150     TapeTime++;
11151
11152     for (i = 0; i < MAX_PLAYERS; i++)
11153     {
11154       struct PlayerInfo *player = &stored_player[i];
11155
11156       if (SHIELD_ON(player))
11157       {
11158         player->shield_normal_time_left--;
11159
11160         if (player->shield_deadly_time_left > 0)
11161           player->shield_deadly_time_left--;
11162       }
11163     }
11164
11165     if (!local_player->LevelSolved && !level.use_step_counter)
11166     {
11167       TimePlayed++;
11168
11169       if (TimeLeft > 0)
11170       {
11171         TimeLeft--;
11172
11173         if (TimeLeft <= 10 && setup.time_limit)
11174           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11175
11176         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11177            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11178
11179         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11180
11181         if (!TimeLeft && setup.time_limit)
11182         {
11183           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11184             level.native_em_level->lev->killed_out_of_time = TRUE;
11185           else
11186             for (i = 0; i < MAX_PLAYERS; i++)
11187               KillPlayer(&stored_player[i]);
11188         }
11189       }
11190       else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit
11191       {
11192         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11193       }
11194
11195       level.native_em_level->lev->time =
11196         (game.no_time_limit ? TimePlayed : TimeLeft);
11197     }
11198
11199     if (tape.recording || tape.playing)
11200       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11201   }
11202
11203   if (tape.recording || tape.playing)
11204     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11205
11206   UpdateAndDisplayGameControlValues();
11207 }
11208
11209 void AdvanceFrameAndPlayerCounters(int player_nr)
11210 {
11211   int i;
11212
11213   // advance frame counters (global frame counter and time frame counter)
11214   FrameCounter++;
11215   TimeFrames++;
11216
11217   // advance player counters (counters for move delay, move animation etc.)
11218   for (i = 0; i < MAX_PLAYERS; i++)
11219   {
11220     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11221     int move_delay_value = stored_player[i].move_delay_value;
11222     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11223
11224     if (!advance_player_counters)       // not all players may be affected
11225       continue;
11226
11227     if (move_frames == 0)       // less than one move per game frame
11228     {
11229       int stepsize = TILEX / move_delay_value;
11230       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11231       int count = (stored_player[i].is_moving ?
11232                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11233
11234       if (count % delay == 0)
11235         move_frames = 1;
11236     }
11237
11238     stored_player[i].Frame += move_frames;
11239
11240     if (stored_player[i].MovPos != 0)
11241       stored_player[i].StepFrame += move_frames;
11242
11243     if (stored_player[i].move_delay > 0)
11244       stored_player[i].move_delay--;
11245
11246     // due to bugs in previous versions, counter must count up, not down
11247     if (stored_player[i].push_delay != -1)
11248       stored_player[i].push_delay++;
11249
11250     if (stored_player[i].drop_delay > 0)
11251       stored_player[i].drop_delay--;
11252
11253     if (stored_player[i].is_dropping_pressed)
11254       stored_player[i].drop_pressed_delay++;
11255   }
11256 }
11257
11258 void StartGameActions(boolean init_network_game, boolean record_tape,
11259                       int random_seed)
11260 {
11261   unsigned int new_random_seed = InitRND(random_seed);
11262
11263   if (record_tape)
11264     TapeStartRecording(new_random_seed);
11265
11266   if (init_network_game)
11267   {
11268     SendToServer_LevelFile();
11269     SendToServer_StartPlaying();
11270
11271     return;
11272   }
11273
11274   InitGame();
11275 }
11276
11277 static void GameActionsExt(void)
11278 {
11279 #if 0
11280   static unsigned int game_frame_delay = 0;
11281 #endif
11282   unsigned int game_frame_delay_value;
11283   byte *recorded_player_action;
11284   byte summarized_player_action = 0;
11285   byte tape_action[MAX_PLAYERS];
11286   int i;
11287
11288   // detect endless loops, caused by custom element programming
11289   if (recursion_loop_detected && recursion_loop_depth == 0)
11290   {
11291     char *message = getStringCat3("Internal Error! Element ",
11292                                   EL_NAME(recursion_loop_element),
11293                                   " caused endless loop! Quit the game?");
11294
11295     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11296           EL_NAME(recursion_loop_element));
11297
11298     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11299
11300     recursion_loop_detected = FALSE;    // if game should be continued
11301
11302     free(message);
11303
11304     return;
11305   }
11306
11307   if (game.restart_level)
11308     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11309
11310   CheckLevelSolved();
11311
11312   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11313     GameWon();
11314
11315   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11316     TapeStop();
11317
11318   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11319     return;
11320
11321   game_frame_delay_value =
11322     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11323
11324   if (tape.playing && tape.warp_forward && !tape.pausing)
11325     game_frame_delay_value = 0;
11326
11327   SetVideoFrameDelay(game_frame_delay_value);
11328
11329 #if 0
11330 #if 0
11331   // ---------- main game synchronization point ----------
11332
11333   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11334
11335   printf("::: skip == %d\n", skip);
11336
11337 #else
11338   // ---------- main game synchronization point ----------
11339
11340   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11341 #endif
11342 #endif
11343
11344   if (network_playing && !network_player_action_received)
11345   {
11346     // try to get network player actions in time
11347
11348     // last chance to get network player actions without main loop delay
11349     HandleNetworking();
11350
11351     // game was quit by network peer
11352     if (game_status != GAME_MODE_PLAYING)
11353       return;
11354
11355     // check if network player actions still missing and game still running
11356     if (!network_player_action_received && !checkGameEnded())
11357       return;           // failed to get network player actions in time
11358
11359     // do not yet reset "network_player_action_received" (for tape.pausing)
11360   }
11361
11362   if (tape.pausing)
11363     return;
11364
11365   // at this point we know that we really continue executing the game
11366
11367   network_player_action_received = FALSE;
11368
11369   // when playing tape, read previously recorded player input from tape data
11370   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11371
11372   local_player->effective_mouse_action = local_player->mouse_action;
11373
11374   if (recorded_player_action != NULL)
11375     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11376                                  recorded_player_action);
11377
11378   // TapePlayAction() may return NULL when toggling to "pause before death"
11379   if (tape.pausing)
11380     return;
11381
11382   if (tape.set_centered_player)
11383   {
11384     game.centered_player_nr_next = tape.centered_player_nr_next;
11385     game.set_centered_player = TRUE;
11386   }
11387
11388   for (i = 0; i < MAX_PLAYERS; i++)
11389   {
11390     summarized_player_action |= stored_player[i].action;
11391
11392     if (!network_playing && (game.team_mode || tape.playing))
11393       stored_player[i].effective_action = stored_player[i].action;
11394   }
11395
11396   if (network_playing && !checkGameEnded())
11397     SendToServer_MovePlayer(summarized_player_action);
11398
11399   // summarize all actions at local players mapped input device position
11400   // (this allows using different input devices in single player mode)
11401   if (!network.enabled && !game.team_mode)
11402     stored_player[map_player_action[local_player->index_nr]].effective_action =
11403       summarized_player_action;
11404
11405   if (tape.recording &&
11406       setup.team_mode &&
11407       setup.input_on_focus &&
11408       game.centered_player_nr != -1)
11409   {
11410     for (i = 0; i < MAX_PLAYERS; i++)
11411       stored_player[i].effective_action =
11412         (i == game.centered_player_nr ? summarized_player_action : 0);
11413   }
11414
11415   if (recorded_player_action != NULL)
11416     for (i = 0; i < MAX_PLAYERS; i++)
11417       stored_player[i].effective_action = recorded_player_action[i];
11418
11419   for (i = 0; i < MAX_PLAYERS; i++)
11420   {
11421     tape_action[i] = stored_player[i].effective_action;
11422
11423     /* (this may happen in the RND game engine if a player was not present on
11424        the playfield on level start, but appeared later from a custom element */
11425     if (setup.team_mode &&
11426         tape.recording &&
11427         tape_action[i] &&
11428         !tape.player_participates[i])
11429       tape.player_participates[i] = TRUE;
11430   }
11431
11432   SetTapeActionFromMouseAction(tape_action,
11433                                &local_player->effective_mouse_action);
11434
11435   // only record actions from input devices, but not programmed actions
11436   if (tape.recording)
11437     TapeRecordAction(tape_action);
11438
11439 #if USE_NEW_PLAYER_ASSIGNMENTS
11440   // !!! also map player actions in single player mode !!!
11441   // if (game.team_mode)
11442   if (1)
11443   {
11444     byte mapped_action[MAX_PLAYERS];
11445
11446 #if DEBUG_PLAYER_ACTIONS
11447     printf(":::");
11448     for (i = 0; i < MAX_PLAYERS; i++)
11449       printf(" %d, ", stored_player[i].effective_action);
11450 #endif
11451
11452     for (i = 0; i < MAX_PLAYERS; i++)
11453       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11454
11455     for (i = 0; i < MAX_PLAYERS; i++)
11456       stored_player[i].effective_action = mapped_action[i];
11457
11458 #if DEBUG_PLAYER_ACTIONS
11459     printf(" =>");
11460     for (i = 0; i < MAX_PLAYERS; i++)
11461       printf(" %d, ", stored_player[i].effective_action);
11462     printf("\n");
11463 #endif
11464   }
11465 #if DEBUG_PLAYER_ACTIONS
11466   else
11467   {
11468     printf(":::");
11469     for (i = 0; i < MAX_PLAYERS; i++)
11470       printf(" %d, ", stored_player[i].effective_action);
11471     printf("\n");
11472   }
11473 #endif
11474 #endif
11475
11476   for (i = 0; i < MAX_PLAYERS; i++)
11477   {
11478     // allow engine snapshot in case of changed movement attempt
11479     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11480         (stored_player[i].effective_action & KEY_MOTION))
11481       game.snapshot.changed_action = TRUE;
11482
11483     // allow engine snapshot in case of snapping/dropping attempt
11484     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11485         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11486       game.snapshot.changed_action = TRUE;
11487
11488     game.snapshot.last_action[i] = stored_player[i].effective_action;
11489   }
11490
11491   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11492   {
11493     GameActions_EM_Main();
11494   }
11495   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11496   {
11497     GameActions_SP_Main();
11498   }
11499   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11500   {
11501     GameActions_MM_Main();
11502   }
11503   else
11504   {
11505     GameActions_RND_Main();
11506   }
11507
11508   BlitScreenToBitmap(backbuffer);
11509
11510   CheckLevelSolved();
11511   CheckLevelTime();
11512
11513   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11514
11515   if (global.show_frames_per_second)
11516   {
11517     static unsigned int fps_counter = 0;
11518     static int fps_frames = 0;
11519     unsigned int fps_delay_ms = Counter() - fps_counter;
11520
11521     fps_frames++;
11522
11523     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11524     {
11525       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11526
11527       fps_frames = 0;
11528       fps_counter = Counter();
11529
11530       // always draw FPS to screen after FPS value was updated
11531       redraw_mask |= REDRAW_FPS;
11532     }
11533
11534     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11535     if (GetDrawDeactivationMask() == REDRAW_NONE)
11536       redraw_mask |= REDRAW_FPS;
11537   }
11538 }
11539
11540 static void GameActions_CheckSaveEngineSnapshot(void)
11541 {
11542   if (!game.snapshot.save_snapshot)
11543     return;
11544
11545   // clear flag for saving snapshot _before_ saving snapshot
11546   game.snapshot.save_snapshot = FALSE;
11547
11548   SaveEngineSnapshotToList();
11549 }
11550
11551 void GameActions(void)
11552 {
11553   GameActionsExt();
11554
11555   GameActions_CheckSaveEngineSnapshot();
11556 }
11557
11558 void GameActions_EM_Main(void)
11559 {
11560   byte effective_action[MAX_PLAYERS];
11561   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11562   int i;
11563
11564   for (i = 0; i < MAX_PLAYERS; i++)
11565     effective_action[i] = stored_player[i].effective_action;
11566
11567   GameActions_EM(effective_action, warp_mode);
11568 }
11569
11570 void GameActions_SP_Main(void)
11571 {
11572   byte effective_action[MAX_PLAYERS];
11573   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11574   int i;
11575
11576   for (i = 0; i < MAX_PLAYERS; i++)
11577     effective_action[i] = stored_player[i].effective_action;
11578
11579   GameActions_SP(effective_action, warp_mode);
11580
11581   for (i = 0; i < MAX_PLAYERS; i++)
11582   {
11583     if (stored_player[i].force_dropping)
11584       stored_player[i].action |= KEY_BUTTON_DROP;
11585
11586     stored_player[i].force_dropping = FALSE;
11587   }
11588 }
11589
11590 void GameActions_MM_Main(void)
11591 {
11592   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11593
11594   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11595 }
11596
11597 void GameActions_RND_Main(void)
11598 {
11599   GameActions_RND();
11600 }
11601
11602 void GameActions_RND(void)
11603 {
11604   int magic_wall_x = 0, magic_wall_y = 0;
11605   int i, x, y, element, graphic, last_gfx_frame;
11606
11607   InitPlayfieldScanModeVars();
11608
11609   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11610   {
11611     SCAN_PLAYFIELD(x, y)
11612     {
11613       ChangeCount[x][y] = 0;
11614       ChangeEvent[x][y] = -1;
11615     }
11616   }
11617
11618   if (game.set_centered_player)
11619   {
11620     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11621
11622     // switching to "all players" only possible if all players fit to screen
11623     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11624     {
11625       game.centered_player_nr_next = game.centered_player_nr;
11626       game.set_centered_player = FALSE;
11627     }
11628
11629     // do not switch focus to non-existing (or non-active) player
11630     if (game.centered_player_nr_next >= 0 &&
11631         !stored_player[game.centered_player_nr_next].active)
11632     {
11633       game.centered_player_nr_next = game.centered_player_nr;
11634       game.set_centered_player = FALSE;
11635     }
11636   }
11637
11638   if (game.set_centered_player &&
11639       ScreenMovPos == 0)        // screen currently aligned at tile position
11640   {
11641     int sx, sy;
11642
11643     if (game.centered_player_nr_next == -1)
11644     {
11645       setScreenCenteredToAllPlayers(&sx, &sy);
11646     }
11647     else
11648     {
11649       sx = stored_player[game.centered_player_nr_next].jx;
11650       sy = stored_player[game.centered_player_nr_next].jy;
11651     }
11652
11653     game.centered_player_nr = game.centered_player_nr_next;
11654     game.set_centered_player = FALSE;
11655
11656     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11657     DrawGameDoorValues();
11658   }
11659
11660   for (i = 0; i < MAX_PLAYERS; i++)
11661   {
11662     int actual_player_action = stored_player[i].effective_action;
11663
11664 #if 1
11665     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11666        - rnd_equinox_tetrachloride 048
11667        - rnd_equinox_tetrachloride_ii 096
11668        - rnd_emanuel_schmieg 002
11669        - doctor_sloan_ww 001, 020
11670     */
11671     if (stored_player[i].MovPos == 0)
11672       CheckGravityMovement(&stored_player[i]);
11673 #endif
11674
11675     // overwrite programmed action with tape action
11676     if (stored_player[i].programmed_action)
11677       actual_player_action = stored_player[i].programmed_action;
11678
11679     PlayerActions(&stored_player[i], actual_player_action);
11680
11681     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11682   }
11683
11684   ScrollScreen(NULL, SCROLL_GO_ON);
11685
11686   /* for backwards compatibility, the following code emulates a fixed bug that
11687      occured when pushing elements (causing elements that just made their last
11688      pushing step to already (if possible) make their first falling step in the
11689      same game frame, which is bad); this code is also needed to use the famous
11690      "spring push bug" which is used in older levels and might be wanted to be
11691      used also in newer levels, but in this case the buggy pushing code is only
11692      affecting the "spring" element and no other elements */
11693
11694   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11695   {
11696     for (i = 0; i < MAX_PLAYERS; i++)
11697     {
11698       struct PlayerInfo *player = &stored_player[i];
11699       int x = player->jx;
11700       int y = player->jy;
11701
11702       if (player->active && player->is_pushing && player->is_moving &&
11703           IS_MOVING(x, y) &&
11704           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11705            Feld[x][y] == EL_SPRING))
11706       {
11707         ContinueMoving(x, y);
11708
11709         // continue moving after pushing (this is actually a bug)
11710         if (!IS_MOVING(x, y))
11711           Stop[x][y] = FALSE;
11712       }
11713     }
11714   }
11715
11716   SCAN_PLAYFIELD(x, y)
11717   {
11718     Last[x][y] = Feld[x][y];
11719
11720     ChangeCount[x][y] = 0;
11721     ChangeEvent[x][y] = -1;
11722
11723     // this must be handled before main playfield loop
11724     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11725     {
11726       MovDelay[x][y]--;
11727       if (MovDelay[x][y] <= 0)
11728         RemoveField(x, y);
11729     }
11730
11731     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11732     {
11733       MovDelay[x][y]--;
11734       if (MovDelay[x][y] <= 0)
11735       {
11736         RemoveField(x, y);
11737         TEST_DrawLevelField(x, y);
11738
11739         TestIfElementTouchesCustomElement(x, y);        // for empty space
11740       }
11741     }
11742
11743 #if DEBUG
11744     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11745     {
11746       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11747       printf("GameActions(): This should never happen!\n");
11748
11749       ChangePage[x][y] = -1;
11750     }
11751 #endif
11752
11753     Stop[x][y] = FALSE;
11754     if (WasJustMoving[x][y] > 0)
11755       WasJustMoving[x][y]--;
11756     if (WasJustFalling[x][y] > 0)
11757       WasJustFalling[x][y]--;
11758     if (CheckCollision[x][y] > 0)
11759       CheckCollision[x][y]--;
11760     if (CheckImpact[x][y] > 0)
11761       CheckImpact[x][y]--;
11762
11763     GfxFrame[x][y]++;
11764
11765     /* reset finished pushing action (not done in ContinueMoving() to allow
11766        continuous pushing animation for elements with zero push delay) */
11767     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11768     {
11769       ResetGfxAnimation(x, y);
11770       TEST_DrawLevelField(x, y);
11771     }
11772
11773 #if DEBUG
11774     if (IS_BLOCKED(x, y))
11775     {
11776       int oldx, oldy;
11777
11778       Blocked2Moving(x, y, &oldx, &oldy);
11779       if (!IS_MOVING(oldx, oldy))
11780       {
11781         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11782         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11783         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11784         printf("GameActions(): This should never happen!\n");
11785       }
11786     }
11787 #endif
11788   }
11789
11790   SCAN_PLAYFIELD(x, y)
11791   {
11792     element = Feld[x][y];
11793     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11794     last_gfx_frame = GfxFrame[x][y];
11795
11796     ResetGfxFrame(x, y);
11797
11798     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11799       DrawLevelGraphicAnimation(x, y, graphic);
11800
11801     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11802         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11803       ResetRandomAnimationValue(x, y);
11804
11805     SetRandomAnimationValue(x, y);
11806
11807     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11808
11809     if (IS_INACTIVE(element))
11810     {
11811       if (IS_ANIMATED(graphic))
11812         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11813
11814       continue;
11815     }
11816
11817     // this may take place after moving, so 'element' may have changed
11818     if (IS_CHANGING(x, y) &&
11819         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11820     {
11821       int page = element_info[element].event_page_nr[CE_DELAY];
11822
11823       HandleElementChange(x, y, page);
11824
11825       element = Feld[x][y];
11826       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11827     }
11828
11829     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11830     {
11831       StartMoving(x, y);
11832
11833       element = Feld[x][y];
11834       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11835
11836       if (IS_ANIMATED(graphic) &&
11837           !IS_MOVING(x, y) &&
11838           !Stop[x][y])
11839         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11840
11841       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11842         TEST_DrawTwinkleOnField(x, y);
11843     }
11844     else if (element == EL_ACID)
11845     {
11846       if (!Stop[x][y])
11847         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11848     }
11849     else if ((element == EL_EXIT_OPEN ||
11850               element == EL_EM_EXIT_OPEN ||
11851               element == EL_SP_EXIT_OPEN ||
11852               element == EL_STEEL_EXIT_OPEN ||
11853               element == EL_EM_STEEL_EXIT_OPEN ||
11854               element == EL_SP_TERMINAL ||
11855               element == EL_SP_TERMINAL_ACTIVE ||
11856               element == EL_EXTRA_TIME ||
11857               element == EL_SHIELD_NORMAL ||
11858               element == EL_SHIELD_DEADLY) &&
11859              IS_ANIMATED(graphic))
11860       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11861     else if (IS_MOVING(x, y))
11862       ContinueMoving(x, y);
11863     else if (IS_ACTIVE_BOMB(element))
11864       CheckDynamite(x, y);
11865     else if (element == EL_AMOEBA_GROWING)
11866       AmoebeWaechst(x, y);
11867     else if (element == EL_AMOEBA_SHRINKING)
11868       AmoebaDisappearing(x, y);
11869
11870 #if !USE_NEW_AMOEBA_CODE
11871     else if (IS_AMOEBALIVE(element))
11872       AmoebeAbleger(x, y);
11873 #endif
11874
11875     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11876       Life(x, y);
11877     else if (element == EL_EXIT_CLOSED)
11878       CheckExit(x, y);
11879     else if (element == EL_EM_EXIT_CLOSED)
11880       CheckExitEM(x, y);
11881     else if (element == EL_STEEL_EXIT_CLOSED)
11882       CheckExitSteel(x, y);
11883     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11884       CheckExitSteelEM(x, y);
11885     else if (element == EL_SP_EXIT_CLOSED)
11886       CheckExitSP(x, y);
11887     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11888              element == EL_EXPANDABLE_STEELWALL_GROWING)
11889       MauerWaechst(x, y);
11890     else if (element == EL_EXPANDABLE_WALL ||
11891              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11892              element == EL_EXPANDABLE_WALL_VERTICAL ||
11893              element == EL_EXPANDABLE_WALL_ANY ||
11894              element == EL_BD_EXPANDABLE_WALL)
11895       MauerAbleger(x, y);
11896     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11897              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11898              element == EL_EXPANDABLE_STEELWALL_ANY)
11899       MauerAblegerStahl(x, y);
11900     else if (element == EL_FLAMES)
11901       CheckForDragon(x, y);
11902     else if (element == EL_EXPLOSION)
11903       ; // drawing of correct explosion animation is handled separately
11904     else if (element == EL_ELEMENT_SNAPPING ||
11905              element == EL_DIAGONAL_SHRINKING ||
11906              element == EL_DIAGONAL_GROWING)
11907     {
11908       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11909
11910       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11911     }
11912     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11913       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11914
11915     if (IS_BELT_ACTIVE(element))
11916       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11917
11918     if (game.magic_wall_active)
11919     {
11920       int jx = local_player->jx, jy = local_player->jy;
11921
11922       // play the element sound at the position nearest to the player
11923       if ((element == EL_MAGIC_WALL_FULL ||
11924            element == EL_MAGIC_WALL_ACTIVE ||
11925            element == EL_MAGIC_WALL_EMPTYING ||
11926            element == EL_BD_MAGIC_WALL_FULL ||
11927            element == EL_BD_MAGIC_WALL_ACTIVE ||
11928            element == EL_BD_MAGIC_WALL_EMPTYING ||
11929            element == EL_DC_MAGIC_WALL_FULL ||
11930            element == EL_DC_MAGIC_WALL_ACTIVE ||
11931            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11932           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11933       {
11934         magic_wall_x = x;
11935         magic_wall_y = y;
11936       }
11937     }
11938   }
11939
11940 #if USE_NEW_AMOEBA_CODE
11941   // new experimental amoeba growth stuff
11942   if (!(FrameCounter % 8))
11943   {
11944     static unsigned int random = 1684108901;
11945
11946     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11947     {
11948       x = RND(lev_fieldx);
11949       y = RND(lev_fieldy);
11950       element = Feld[x][y];
11951
11952       if (!IS_PLAYER(x,y) &&
11953           (element == EL_EMPTY ||
11954            CAN_GROW_INTO(element) ||
11955            element == EL_QUICKSAND_EMPTY ||
11956            element == EL_QUICKSAND_FAST_EMPTY ||
11957            element == EL_ACID_SPLASH_LEFT ||
11958            element == EL_ACID_SPLASH_RIGHT))
11959       {
11960         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11961             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11962             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11963             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11964           Feld[x][y] = EL_AMOEBA_DROP;
11965       }
11966
11967       random = random * 129 + 1;
11968     }
11969   }
11970 #endif
11971
11972   game.explosions_delayed = FALSE;
11973
11974   SCAN_PLAYFIELD(x, y)
11975   {
11976     element = Feld[x][y];
11977
11978     if (ExplodeField[x][y])
11979       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11980     else if (element == EL_EXPLOSION)
11981       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11982
11983     ExplodeField[x][y] = EX_TYPE_NONE;
11984   }
11985
11986   game.explosions_delayed = TRUE;
11987
11988   if (game.magic_wall_active)
11989   {
11990     if (!(game.magic_wall_time_left % 4))
11991     {
11992       int element = Feld[magic_wall_x][magic_wall_y];
11993
11994       if (element == EL_BD_MAGIC_WALL_FULL ||
11995           element == EL_BD_MAGIC_WALL_ACTIVE ||
11996           element == EL_BD_MAGIC_WALL_EMPTYING)
11997         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11998       else if (element == EL_DC_MAGIC_WALL_FULL ||
11999                element == EL_DC_MAGIC_WALL_ACTIVE ||
12000                element == EL_DC_MAGIC_WALL_EMPTYING)
12001         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12002       else
12003         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12004     }
12005
12006     if (game.magic_wall_time_left > 0)
12007     {
12008       game.magic_wall_time_left--;
12009
12010       if (!game.magic_wall_time_left)
12011       {
12012         SCAN_PLAYFIELD(x, y)
12013         {
12014           element = Feld[x][y];
12015
12016           if (element == EL_MAGIC_WALL_ACTIVE ||
12017               element == EL_MAGIC_WALL_FULL)
12018           {
12019             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12020             TEST_DrawLevelField(x, y);
12021           }
12022           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12023                    element == EL_BD_MAGIC_WALL_FULL)
12024           {
12025             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12026             TEST_DrawLevelField(x, y);
12027           }
12028           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12029                    element == EL_DC_MAGIC_WALL_FULL)
12030           {
12031             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12032             TEST_DrawLevelField(x, y);
12033           }
12034         }
12035
12036         game.magic_wall_active = FALSE;
12037       }
12038     }
12039   }
12040
12041   if (game.light_time_left > 0)
12042   {
12043     game.light_time_left--;
12044
12045     if (game.light_time_left == 0)
12046       RedrawAllLightSwitchesAndInvisibleElements();
12047   }
12048
12049   if (game.timegate_time_left > 0)
12050   {
12051     game.timegate_time_left--;
12052
12053     if (game.timegate_time_left == 0)
12054       CloseAllOpenTimegates();
12055   }
12056
12057   if (game.lenses_time_left > 0)
12058   {
12059     game.lenses_time_left--;
12060
12061     if (game.lenses_time_left == 0)
12062       RedrawAllInvisibleElementsForLenses();
12063   }
12064
12065   if (game.magnify_time_left > 0)
12066   {
12067     game.magnify_time_left--;
12068
12069     if (game.magnify_time_left == 0)
12070       RedrawAllInvisibleElementsForMagnifier();
12071   }
12072
12073   for (i = 0; i < MAX_PLAYERS; i++)
12074   {
12075     struct PlayerInfo *player = &stored_player[i];
12076
12077     if (SHIELD_ON(player))
12078     {
12079       if (player->shield_deadly_time_left)
12080         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12081       else if (player->shield_normal_time_left)
12082         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12083     }
12084   }
12085
12086 #if USE_DELAYED_GFX_REDRAW
12087   SCAN_PLAYFIELD(x, y)
12088   {
12089     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12090     {
12091       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12092          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12093
12094       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12095         DrawLevelField(x, y);
12096
12097       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12098         DrawLevelFieldCrumbled(x, y);
12099
12100       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12101         DrawLevelFieldCrumbledNeighbours(x, y);
12102
12103       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12104         DrawTwinkleOnField(x, y);
12105     }
12106
12107     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12108   }
12109 #endif
12110
12111   DrawAllPlayers();
12112   PlayAllPlayersSound();
12113
12114   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12115   {
12116     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12117
12118     local_player->show_envelope = 0;
12119   }
12120
12121   // use random number generator in every frame to make it less predictable
12122   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12123     RND(1);
12124 }
12125
12126 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12127 {
12128   int min_x = x, min_y = y, max_x = x, max_y = y;
12129   int i;
12130
12131   for (i = 0; i < MAX_PLAYERS; i++)
12132   {
12133     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12134
12135     if (!stored_player[i].active || &stored_player[i] == player)
12136       continue;
12137
12138     min_x = MIN(min_x, jx);
12139     min_y = MIN(min_y, jy);
12140     max_x = MAX(max_x, jx);
12141     max_y = MAX(max_y, jy);
12142   }
12143
12144   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12145 }
12146
12147 static boolean AllPlayersInVisibleScreen(void)
12148 {
12149   int i;
12150
12151   for (i = 0; i < MAX_PLAYERS; i++)
12152   {
12153     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12154
12155     if (!stored_player[i].active)
12156       continue;
12157
12158     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12159       return FALSE;
12160   }
12161
12162   return TRUE;
12163 }
12164
12165 void ScrollLevel(int dx, int dy)
12166 {
12167   int scroll_offset = 2 * TILEX_VAR;
12168   int x, y;
12169
12170   BlitBitmap(drawto_field, drawto_field,
12171              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12172              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12173              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12174              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12175              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12176              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12177
12178   if (dx != 0)
12179   {
12180     x = (dx == 1 ? BX1 : BX2);
12181     for (y = BY1; y <= BY2; y++)
12182       DrawScreenField(x, y);
12183   }
12184
12185   if (dy != 0)
12186   {
12187     y = (dy == 1 ? BY1 : BY2);
12188     for (x = BX1; x <= BX2; x++)
12189       DrawScreenField(x, y);
12190   }
12191
12192   redraw_mask |= REDRAW_FIELD;
12193 }
12194
12195 static boolean canFallDown(struct PlayerInfo *player)
12196 {
12197   int jx = player->jx, jy = player->jy;
12198
12199   return (IN_LEV_FIELD(jx, jy + 1) &&
12200           (IS_FREE(jx, jy + 1) ||
12201            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12202           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12203           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12204 }
12205
12206 static boolean canPassField(int x, int y, int move_dir)
12207 {
12208   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12209   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12210   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12211   int nextx = x + dx;
12212   int nexty = y + dy;
12213   int element = Feld[x][y];
12214
12215   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12216           !CAN_MOVE(element) &&
12217           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12218           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12219           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12220 }
12221
12222 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12223 {
12224   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12225   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12226   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12227   int newx = x + dx;
12228   int newy = y + dy;
12229
12230   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12231           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12232           (IS_DIGGABLE(Feld[newx][newy]) ||
12233            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12234            canPassField(newx, newy, move_dir)));
12235 }
12236
12237 static void CheckGravityMovement(struct PlayerInfo *player)
12238 {
12239   if (player->gravity && !player->programmed_action)
12240   {
12241     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12242     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12243     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12244     int jx = player->jx, jy = player->jy;
12245     boolean player_is_moving_to_valid_field =
12246       (!player_is_snapping &&
12247        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12248         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12249     boolean player_can_fall_down = canFallDown(player);
12250
12251     if (player_can_fall_down &&
12252         !player_is_moving_to_valid_field)
12253       player->programmed_action = MV_DOWN;
12254   }
12255 }
12256
12257 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12258 {
12259   return CheckGravityMovement(player);
12260
12261   if (player->gravity && !player->programmed_action)
12262   {
12263     int jx = player->jx, jy = player->jy;
12264     boolean field_under_player_is_free =
12265       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12266     boolean player_is_standing_on_valid_field =
12267       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12268        (IS_WALKABLE(Feld[jx][jy]) &&
12269         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12270
12271     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12272       player->programmed_action = MV_DOWN;
12273   }
12274 }
12275
12276 /*
12277   MovePlayerOneStep()
12278   -----------------------------------------------------------------------------
12279   dx, dy:               direction (non-diagonal) to try to move the player to
12280   real_dx, real_dy:     direction as read from input device (can be diagonal)
12281 */
12282
12283 boolean MovePlayerOneStep(struct PlayerInfo *player,
12284                           int dx, int dy, int real_dx, int real_dy)
12285 {
12286   int jx = player->jx, jy = player->jy;
12287   int new_jx = jx + dx, new_jy = jy + dy;
12288   int can_move;
12289   boolean player_can_move = !player->cannot_move;
12290
12291   if (!player->active || (!dx && !dy))
12292     return MP_NO_ACTION;
12293
12294   player->MovDir = (dx < 0 ? MV_LEFT :
12295                     dx > 0 ? MV_RIGHT :
12296                     dy < 0 ? MV_UP :
12297                     dy > 0 ? MV_DOWN :  MV_NONE);
12298
12299   if (!IN_LEV_FIELD(new_jx, new_jy))
12300     return MP_NO_ACTION;
12301
12302   if (!player_can_move)
12303   {
12304     if (player->MovPos == 0)
12305     {
12306       player->is_moving = FALSE;
12307       player->is_digging = FALSE;
12308       player->is_collecting = FALSE;
12309       player->is_snapping = FALSE;
12310       player->is_pushing = FALSE;
12311     }
12312   }
12313
12314   if (!network.enabled && game.centered_player_nr == -1 &&
12315       !AllPlayersInSight(player, new_jx, new_jy))
12316     return MP_NO_ACTION;
12317
12318   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12319   if (can_move != MP_MOVING)
12320     return can_move;
12321
12322   // check if DigField() has caused relocation of the player
12323   if (player->jx != jx || player->jy != jy)
12324     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12325
12326   StorePlayer[jx][jy] = 0;
12327   player->last_jx = jx;
12328   player->last_jy = jy;
12329   player->jx = new_jx;
12330   player->jy = new_jy;
12331   StorePlayer[new_jx][new_jy] = player->element_nr;
12332
12333   if (player->move_delay_value_next != -1)
12334   {
12335     player->move_delay_value = player->move_delay_value_next;
12336     player->move_delay_value_next = -1;
12337   }
12338
12339   player->MovPos =
12340     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12341
12342   player->step_counter++;
12343
12344   PlayerVisit[jx][jy] = FrameCounter;
12345
12346   player->is_moving = TRUE;
12347
12348 #if 1
12349   // should better be called in MovePlayer(), but this breaks some tapes
12350   ScrollPlayer(player, SCROLL_INIT);
12351 #endif
12352
12353   return MP_MOVING;
12354 }
12355
12356 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12357 {
12358   int jx = player->jx, jy = player->jy;
12359   int old_jx = jx, old_jy = jy;
12360   int moved = MP_NO_ACTION;
12361
12362   if (!player->active)
12363     return FALSE;
12364
12365   if (!dx && !dy)
12366   {
12367     if (player->MovPos == 0)
12368     {
12369       player->is_moving = FALSE;
12370       player->is_digging = FALSE;
12371       player->is_collecting = FALSE;
12372       player->is_snapping = FALSE;
12373       player->is_pushing = FALSE;
12374     }
12375
12376     return FALSE;
12377   }
12378
12379   if (player->move_delay > 0)
12380     return FALSE;
12381
12382   player->move_delay = -1;              // set to "uninitialized" value
12383
12384   // store if player is automatically moved to next field
12385   player->is_auto_moving = (player->programmed_action != MV_NONE);
12386
12387   // remove the last programmed player action
12388   player->programmed_action = 0;
12389
12390   if (player->MovPos)
12391   {
12392     // should only happen if pre-1.2 tape recordings are played
12393     // this is only for backward compatibility
12394
12395     int original_move_delay_value = player->move_delay_value;
12396
12397 #if DEBUG
12398     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12399            tape.counter);
12400 #endif
12401
12402     // scroll remaining steps with finest movement resolution
12403     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12404
12405     while (player->MovPos)
12406     {
12407       ScrollPlayer(player, SCROLL_GO_ON);
12408       ScrollScreen(NULL, SCROLL_GO_ON);
12409
12410       AdvanceFrameAndPlayerCounters(player->index_nr);
12411
12412       DrawAllPlayers();
12413       BackToFront_WithFrameDelay(0);
12414     }
12415
12416     player->move_delay_value = original_move_delay_value;
12417   }
12418
12419   player->is_active = FALSE;
12420
12421   if (player->last_move_dir & MV_HORIZONTAL)
12422   {
12423     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12424       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12425   }
12426   else
12427   {
12428     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12429       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12430   }
12431
12432   if (!moved && !player->is_active)
12433   {
12434     player->is_moving = FALSE;
12435     player->is_digging = FALSE;
12436     player->is_collecting = FALSE;
12437     player->is_snapping = FALSE;
12438     player->is_pushing = FALSE;
12439   }
12440
12441   jx = player->jx;
12442   jy = player->jy;
12443
12444   if (moved & MP_MOVING && !ScreenMovPos &&
12445       (player->index_nr == game.centered_player_nr ||
12446        game.centered_player_nr == -1))
12447   {
12448     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12449     int offset = game.scroll_delay_value;
12450
12451     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12452     {
12453       // actual player has left the screen -- scroll in that direction
12454       if (jx != old_jx)         // player has moved horizontally
12455         scroll_x += (jx - old_jx);
12456       else                      // player has moved vertically
12457         scroll_y += (jy - old_jy);
12458     }
12459     else
12460     {
12461       if (jx != old_jx)         // player has moved horizontally
12462       {
12463         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12464             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12465           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12466
12467         // don't scroll over playfield boundaries
12468         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12469           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12470
12471         // don't scroll more than one field at a time
12472         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12473
12474         // don't scroll against the player's moving direction
12475         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12476             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12477           scroll_x = old_scroll_x;
12478       }
12479       else                      // player has moved vertically
12480       {
12481         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12482             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12483           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12484
12485         // don't scroll over playfield boundaries
12486         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12487           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12488
12489         // don't scroll more than one field at a time
12490         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12491
12492         // don't scroll against the player's moving direction
12493         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12494             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12495           scroll_y = old_scroll_y;
12496       }
12497     }
12498
12499     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12500     {
12501       if (!network.enabled && game.centered_player_nr == -1 &&
12502           !AllPlayersInVisibleScreen())
12503       {
12504         scroll_x = old_scroll_x;
12505         scroll_y = old_scroll_y;
12506       }
12507       else
12508       {
12509         ScrollScreen(player, SCROLL_INIT);
12510         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12511       }
12512     }
12513   }
12514
12515   player->StepFrame = 0;
12516
12517   if (moved & MP_MOVING)
12518   {
12519     if (old_jx != jx && old_jy == jy)
12520       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12521     else if (old_jx == jx && old_jy != jy)
12522       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12523
12524     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12525
12526     player->last_move_dir = player->MovDir;
12527     player->is_moving = TRUE;
12528     player->is_snapping = FALSE;
12529     player->is_switching = FALSE;
12530     player->is_dropping = FALSE;
12531     player->is_dropping_pressed = FALSE;
12532     player->drop_pressed_delay = 0;
12533
12534 #if 0
12535     // should better be called here than above, but this breaks some tapes
12536     ScrollPlayer(player, SCROLL_INIT);
12537 #endif
12538   }
12539   else
12540   {
12541     CheckGravityMovementWhenNotMoving(player);
12542
12543     player->is_moving = FALSE;
12544
12545     /* at this point, the player is allowed to move, but cannot move right now
12546        (e.g. because of something blocking the way) -- ensure that the player
12547        is also allowed to move in the next frame (in old versions before 3.1.1,
12548        the player was forced to wait again for eight frames before next try) */
12549
12550     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12551       player->move_delay = 0;   // allow direct movement in the next frame
12552   }
12553
12554   if (player->move_delay == -1)         // not yet initialized by DigField()
12555     player->move_delay = player->move_delay_value;
12556
12557   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12558   {
12559     TestIfPlayerTouchesBadThing(jx, jy);
12560     TestIfPlayerTouchesCustomElement(jx, jy);
12561   }
12562
12563   if (!player->active)
12564     RemovePlayer(player);
12565
12566   return moved;
12567 }
12568
12569 void ScrollPlayer(struct PlayerInfo *player, int mode)
12570 {
12571   int jx = player->jx, jy = player->jy;
12572   int last_jx = player->last_jx, last_jy = player->last_jy;
12573   int move_stepsize = TILEX / player->move_delay_value;
12574
12575   if (!player->active)
12576     return;
12577
12578   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12579     return;
12580
12581   if (mode == SCROLL_INIT)
12582   {
12583     player->actual_frame_counter = FrameCounter;
12584     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12585
12586     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12587         Feld[last_jx][last_jy] == EL_EMPTY)
12588     {
12589       int last_field_block_delay = 0;   // start with no blocking at all
12590       int block_delay_adjustment = player->block_delay_adjustment;
12591
12592       // if player blocks last field, add delay for exactly one move
12593       if (player->block_last_field)
12594       {
12595         last_field_block_delay += player->move_delay_value;
12596
12597         // when blocking enabled, prevent moving up despite gravity
12598         if (player->gravity && player->MovDir == MV_UP)
12599           block_delay_adjustment = -1;
12600       }
12601
12602       // add block delay adjustment (also possible when not blocking)
12603       last_field_block_delay += block_delay_adjustment;
12604
12605       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12606       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12607     }
12608
12609     if (player->MovPos != 0)    // player has not yet reached destination
12610       return;
12611   }
12612   else if (!FrameReached(&player->actual_frame_counter, 1))
12613     return;
12614
12615   if (player->MovPos != 0)
12616   {
12617     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12618     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12619
12620     // before DrawPlayer() to draw correct player graphic for this case
12621     if (player->MovPos == 0)
12622       CheckGravityMovement(player);
12623   }
12624
12625   if (player->MovPos == 0)      // player reached destination field
12626   {
12627     if (player->move_delay_reset_counter > 0)
12628     {
12629       player->move_delay_reset_counter--;
12630
12631       if (player->move_delay_reset_counter == 0)
12632       {
12633         // continue with normal speed after quickly moving through gate
12634         HALVE_PLAYER_SPEED(player);
12635
12636         // be able to make the next move without delay
12637         player->move_delay = 0;
12638       }
12639     }
12640
12641     player->last_jx = jx;
12642     player->last_jy = jy;
12643
12644     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12645         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12646         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12647         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12648         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12649         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12650         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12651         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12652     {
12653       ExitPlayer(player);
12654
12655       if ((local_player->friends_still_needed == 0 ||
12656            IS_SP_ELEMENT(Feld[jx][jy])) &&
12657           AllPlayersGone)
12658         PlayerWins(local_player);
12659     }
12660
12661     // this breaks one level: "machine", level 000
12662     {
12663       int move_direction = player->MovDir;
12664       int enter_side = MV_DIR_OPPOSITE(move_direction);
12665       int leave_side = move_direction;
12666       int old_jx = last_jx;
12667       int old_jy = last_jy;
12668       int old_element = Feld[old_jx][old_jy];
12669       int new_element = Feld[jx][jy];
12670
12671       if (IS_CUSTOM_ELEMENT(old_element))
12672         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12673                                    CE_LEFT_BY_PLAYER,
12674                                    player->index_bit, leave_side);
12675
12676       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12677                                           CE_PLAYER_LEAVES_X,
12678                                           player->index_bit, leave_side);
12679
12680       if (IS_CUSTOM_ELEMENT(new_element))
12681         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12682                                    player->index_bit, enter_side);
12683
12684       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12685                                           CE_PLAYER_ENTERS_X,
12686                                           player->index_bit, enter_side);
12687
12688       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12689                                         CE_MOVE_OF_X, move_direction);
12690     }
12691
12692     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12693     {
12694       TestIfPlayerTouchesBadThing(jx, jy);
12695       TestIfPlayerTouchesCustomElement(jx, jy);
12696
12697       /* needed because pushed element has not yet reached its destination,
12698          so it would trigger a change event at its previous field location */
12699       if (!player->is_pushing)
12700         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12701
12702       if (!player->active)
12703         RemovePlayer(player);
12704     }
12705
12706     if (!local_player->LevelSolved && level.use_step_counter)
12707     {
12708       int i;
12709
12710       TimePlayed++;
12711
12712       if (TimeLeft > 0)
12713       {
12714         TimeLeft--;
12715
12716         if (TimeLeft <= 10 && setup.time_limit)
12717           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12718
12719         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12720
12721         DisplayGameControlValues();
12722
12723         if (!TimeLeft && setup.time_limit)
12724           for (i = 0; i < MAX_PLAYERS; i++)
12725             KillPlayer(&stored_player[i]);
12726       }
12727       else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit
12728       {
12729         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12730
12731         DisplayGameControlValues();
12732       }
12733     }
12734
12735     if (tape.single_step && tape.recording && !tape.pausing &&
12736         !player->programmed_action)
12737       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12738
12739     if (!player->programmed_action)
12740       CheckSaveEngineSnapshot(player);
12741   }
12742 }
12743
12744 void ScrollScreen(struct PlayerInfo *player, int mode)
12745 {
12746   static unsigned int screen_frame_counter = 0;
12747
12748   if (mode == SCROLL_INIT)
12749   {
12750     // set scrolling step size according to actual player's moving speed
12751     ScrollStepSize = TILEX / player->move_delay_value;
12752
12753     screen_frame_counter = FrameCounter;
12754     ScreenMovDir = player->MovDir;
12755     ScreenMovPos = player->MovPos;
12756     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12757     return;
12758   }
12759   else if (!FrameReached(&screen_frame_counter, 1))
12760     return;
12761
12762   if (ScreenMovPos)
12763   {
12764     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12765     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12766     redraw_mask |= REDRAW_FIELD;
12767   }
12768   else
12769     ScreenMovDir = MV_NONE;
12770 }
12771
12772 void TestIfPlayerTouchesCustomElement(int x, int y)
12773 {
12774   static int xy[4][2] =
12775   {
12776     { 0, -1 },
12777     { -1, 0 },
12778     { +1, 0 },
12779     { 0, +1 }
12780   };
12781   static int trigger_sides[4][2] =
12782   {
12783     // center side       border side
12784     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12785     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12786     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12787     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12788   };
12789   static int touch_dir[4] =
12790   {
12791     MV_LEFT | MV_RIGHT,
12792     MV_UP   | MV_DOWN,
12793     MV_UP   | MV_DOWN,
12794     MV_LEFT | MV_RIGHT
12795   };
12796   int center_element = Feld[x][y];      // should always be non-moving!
12797   int i;
12798
12799   for (i = 0; i < NUM_DIRECTIONS; i++)
12800   {
12801     int xx = x + xy[i][0];
12802     int yy = y + xy[i][1];
12803     int center_side = trigger_sides[i][0];
12804     int border_side = trigger_sides[i][1];
12805     int border_element;
12806
12807     if (!IN_LEV_FIELD(xx, yy))
12808       continue;
12809
12810     if (IS_PLAYER(x, y))                // player found at center element
12811     {
12812       struct PlayerInfo *player = PLAYERINFO(x, y);
12813
12814       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12815         border_element = Feld[xx][yy];          // may be moving!
12816       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12817         border_element = Feld[xx][yy];
12818       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12819         border_element = MovingOrBlocked2Element(xx, yy);
12820       else
12821         continue;               // center and border element do not touch
12822
12823       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12824                                  player->index_bit, border_side);
12825       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12826                                           CE_PLAYER_TOUCHES_X,
12827                                           player->index_bit, border_side);
12828
12829       {
12830         /* use player element that is initially defined in the level playfield,
12831            not the player element that corresponds to the runtime player number
12832            (example: a level that contains EL_PLAYER_3 as the only player would
12833            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12834         int player_element = PLAYERINFO(x, y)->initial_element;
12835
12836         CheckElementChangeBySide(xx, yy, border_element, player_element,
12837                                  CE_TOUCHING_X, border_side);
12838       }
12839     }
12840     else if (IS_PLAYER(xx, yy))         // player found at border element
12841     {
12842       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12843
12844       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12845       {
12846         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12847           continue;             // center and border element do not touch
12848       }
12849
12850       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12851                                  player->index_bit, center_side);
12852       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12853                                           CE_PLAYER_TOUCHES_X,
12854                                           player->index_bit, center_side);
12855
12856       {
12857         /* use player element that is initially defined in the level playfield,
12858            not the player element that corresponds to the runtime player number
12859            (example: a level that contains EL_PLAYER_3 as the only player would
12860            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12861         int player_element = PLAYERINFO(xx, yy)->initial_element;
12862
12863         CheckElementChangeBySide(x, y, center_element, player_element,
12864                                  CE_TOUCHING_X, center_side);
12865       }
12866
12867       break;
12868     }
12869   }
12870 }
12871
12872 void TestIfElementTouchesCustomElement(int x, int y)
12873 {
12874   static int xy[4][2] =
12875   {
12876     { 0, -1 },
12877     { -1, 0 },
12878     { +1, 0 },
12879     { 0, +1 }
12880   };
12881   static int trigger_sides[4][2] =
12882   {
12883     // center side      border side
12884     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12885     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12886     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12887     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12888   };
12889   static int touch_dir[4] =
12890   {
12891     MV_LEFT | MV_RIGHT,
12892     MV_UP   | MV_DOWN,
12893     MV_UP   | MV_DOWN,
12894     MV_LEFT | MV_RIGHT
12895   };
12896   boolean change_center_element = FALSE;
12897   int center_element = Feld[x][y];      // should always be non-moving!
12898   int border_element_old[NUM_DIRECTIONS];
12899   int i;
12900
12901   for (i = 0; i < NUM_DIRECTIONS; i++)
12902   {
12903     int xx = x + xy[i][0];
12904     int yy = y + xy[i][1];
12905     int border_element;
12906
12907     border_element_old[i] = -1;
12908
12909     if (!IN_LEV_FIELD(xx, yy))
12910       continue;
12911
12912     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12913       border_element = Feld[xx][yy];    // may be moving!
12914     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12915       border_element = Feld[xx][yy];
12916     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12917       border_element = MovingOrBlocked2Element(xx, yy);
12918     else
12919       continue;                 // center and border element do not touch
12920
12921     border_element_old[i] = border_element;
12922   }
12923
12924   for (i = 0; i < NUM_DIRECTIONS; i++)
12925   {
12926     int xx = x + xy[i][0];
12927     int yy = y + xy[i][1];
12928     int center_side = trigger_sides[i][0];
12929     int border_element = border_element_old[i];
12930
12931     if (border_element == -1)
12932       continue;
12933
12934     // check for change of border element
12935     CheckElementChangeBySide(xx, yy, border_element, center_element,
12936                              CE_TOUCHING_X, center_side);
12937
12938     // (center element cannot be player, so we dont have to check this here)
12939   }
12940
12941   for (i = 0; i < NUM_DIRECTIONS; i++)
12942   {
12943     int xx = x + xy[i][0];
12944     int yy = y + xy[i][1];
12945     int border_side = trigger_sides[i][1];
12946     int border_element = border_element_old[i];
12947
12948     if (border_element == -1)
12949       continue;
12950
12951     // check for change of center element (but change it only once)
12952     if (!change_center_element)
12953       change_center_element =
12954         CheckElementChangeBySide(x, y, center_element, border_element,
12955                                  CE_TOUCHING_X, border_side);
12956
12957     if (IS_PLAYER(xx, yy))
12958     {
12959       /* use player element that is initially defined in the level playfield,
12960          not the player element that corresponds to the runtime player number
12961          (example: a level that contains EL_PLAYER_3 as the only player would
12962          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12963       int player_element = PLAYERINFO(xx, yy)->initial_element;
12964
12965       CheckElementChangeBySide(x, y, center_element, player_element,
12966                                CE_TOUCHING_X, border_side);
12967     }
12968   }
12969 }
12970
12971 void TestIfElementHitsCustomElement(int x, int y, int direction)
12972 {
12973   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12974   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12975   int hitx = x + dx, hity = y + dy;
12976   int hitting_element = Feld[x][y];
12977   int touched_element;
12978
12979   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12980     return;
12981
12982   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12983                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12984
12985   if (IN_LEV_FIELD(hitx, hity))
12986   {
12987     int opposite_direction = MV_DIR_OPPOSITE(direction);
12988     int hitting_side = direction;
12989     int touched_side = opposite_direction;
12990     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12991                           MovDir[hitx][hity] != direction ||
12992                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12993
12994     object_hit = TRUE;
12995
12996     if (object_hit)
12997     {
12998       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12999                                CE_HITTING_X, touched_side);
13000
13001       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13002                                CE_HIT_BY_X, hitting_side);
13003
13004       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13005                                CE_HIT_BY_SOMETHING, opposite_direction);
13006
13007       if (IS_PLAYER(hitx, hity))
13008       {
13009         /* use player element that is initially defined in the level playfield,
13010            not the player element that corresponds to the runtime player number
13011            (example: a level that contains EL_PLAYER_3 as the only player would
13012            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13013         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13014
13015         CheckElementChangeBySide(x, y, hitting_element, player_element,
13016                                  CE_HITTING_X, touched_side);
13017       }
13018     }
13019   }
13020
13021   // "hitting something" is also true when hitting the playfield border
13022   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13023                            CE_HITTING_SOMETHING, direction);
13024 }
13025
13026 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13027 {
13028   int i, kill_x = -1, kill_y = -1;
13029
13030   int bad_element = -1;
13031   static int test_xy[4][2] =
13032   {
13033     { 0, -1 },
13034     { -1, 0 },
13035     { +1, 0 },
13036     { 0, +1 }
13037   };
13038   static int test_dir[4] =
13039   {
13040     MV_UP,
13041     MV_LEFT,
13042     MV_RIGHT,
13043     MV_DOWN
13044   };
13045
13046   for (i = 0; i < NUM_DIRECTIONS; i++)
13047   {
13048     int test_x, test_y, test_move_dir, test_element;
13049
13050     test_x = good_x + test_xy[i][0];
13051     test_y = good_y + test_xy[i][1];
13052
13053     if (!IN_LEV_FIELD(test_x, test_y))
13054       continue;
13055
13056     test_move_dir =
13057       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13058
13059     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13060
13061     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13062        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13063     */
13064     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13065         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13066     {
13067       kill_x = test_x;
13068       kill_y = test_y;
13069       bad_element = test_element;
13070
13071       break;
13072     }
13073   }
13074
13075   if (kill_x != -1 || kill_y != -1)
13076   {
13077     if (IS_PLAYER(good_x, good_y))
13078     {
13079       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13080
13081       if (player->shield_deadly_time_left > 0 &&
13082           !IS_INDESTRUCTIBLE(bad_element))
13083         Bang(kill_x, kill_y);
13084       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13085         KillPlayer(player);
13086     }
13087     else
13088       Bang(good_x, good_y);
13089   }
13090 }
13091
13092 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13093 {
13094   int i, kill_x = -1, kill_y = -1;
13095   int bad_element = Feld[bad_x][bad_y];
13096   static int test_xy[4][2] =
13097   {
13098     { 0, -1 },
13099     { -1, 0 },
13100     { +1, 0 },
13101     { 0, +1 }
13102   };
13103   static int touch_dir[4] =
13104   {
13105     MV_LEFT | MV_RIGHT,
13106     MV_UP   | MV_DOWN,
13107     MV_UP   | MV_DOWN,
13108     MV_LEFT | MV_RIGHT
13109   };
13110   static int test_dir[4] =
13111   {
13112     MV_UP,
13113     MV_LEFT,
13114     MV_RIGHT,
13115     MV_DOWN
13116   };
13117
13118   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13119     return;
13120
13121   for (i = 0; i < NUM_DIRECTIONS; i++)
13122   {
13123     int test_x, test_y, test_move_dir, test_element;
13124
13125     test_x = bad_x + test_xy[i][0];
13126     test_y = bad_y + test_xy[i][1];
13127
13128     if (!IN_LEV_FIELD(test_x, test_y))
13129       continue;
13130
13131     test_move_dir =
13132       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13133
13134     test_element = Feld[test_x][test_y];
13135
13136     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13137        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13138     */
13139     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13140         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13141     {
13142       // good thing is player or penguin that does not move away
13143       if (IS_PLAYER(test_x, test_y))
13144       {
13145         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13146
13147         if (bad_element == EL_ROBOT && player->is_moving)
13148           continue;     // robot does not kill player if he is moving
13149
13150         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13151         {
13152           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13153             continue;           // center and border element do not touch
13154         }
13155
13156         kill_x = test_x;
13157         kill_y = test_y;
13158
13159         break;
13160       }
13161       else if (test_element == EL_PENGUIN)
13162       {
13163         kill_x = test_x;
13164         kill_y = test_y;
13165
13166         break;
13167       }
13168     }
13169   }
13170
13171   if (kill_x != -1 || kill_y != -1)
13172   {
13173     if (IS_PLAYER(kill_x, kill_y))
13174     {
13175       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13176
13177       if (player->shield_deadly_time_left > 0 &&
13178           !IS_INDESTRUCTIBLE(bad_element))
13179         Bang(bad_x, bad_y);
13180       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13181         KillPlayer(player);
13182     }
13183     else
13184       Bang(kill_x, kill_y);
13185   }
13186 }
13187
13188 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13189 {
13190   int bad_element = Feld[bad_x][bad_y];
13191   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13192   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13193   int test_x = bad_x + dx, test_y = bad_y + dy;
13194   int test_move_dir, test_element;
13195   int kill_x = -1, kill_y = -1;
13196
13197   if (!IN_LEV_FIELD(test_x, test_y))
13198     return;
13199
13200   test_move_dir =
13201     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13202
13203   test_element = Feld[test_x][test_y];
13204
13205   if (test_move_dir != bad_move_dir)
13206   {
13207     // good thing can be player or penguin that does not move away
13208     if (IS_PLAYER(test_x, test_y))
13209     {
13210       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13211
13212       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13213          player as being hit when he is moving towards the bad thing, because
13214          the "get hit by" condition would be lost after the player stops) */
13215       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13216         return;         // player moves away from bad thing
13217
13218       kill_x = test_x;
13219       kill_y = test_y;
13220     }
13221     else if (test_element == EL_PENGUIN)
13222     {
13223       kill_x = test_x;
13224       kill_y = test_y;
13225     }
13226   }
13227
13228   if (kill_x != -1 || kill_y != -1)
13229   {
13230     if (IS_PLAYER(kill_x, kill_y))
13231     {
13232       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13233
13234       if (player->shield_deadly_time_left > 0 &&
13235           !IS_INDESTRUCTIBLE(bad_element))
13236         Bang(bad_x, bad_y);
13237       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13238         KillPlayer(player);
13239     }
13240     else
13241       Bang(kill_x, kill_y);
13242   }
13243 }
13244
13245 void TestIfPlayerTouchesBadThing(int x, int y)
13246 {
13247   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13248 }
13249
13250 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13251 {
13252   TestIfGoodThingHitsBadThing(x, y, move_dir);
13253 }
13254
13255 void TestIfBadThingTouchesPlayer(int x, int y)
13256 {
13257   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13258 }
13259
13260 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13261 {
13262   TestIfBadThingHitsGoodThing(x, y, move_dir);
13263 }
13264
13265 void TestIfFriendTouchesBadThing(int x, int y)
13266 {
13267   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13268 }
13269
13270 void TestIfBadThingTouchesFriend(int x, int y)
13271 {
13272   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13273 }
13274
13275 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13276 {
13277   int i, kill_x = bad_x, kill_y = bad_y;
13278   static int xy[4][2] =
13279   {
13280     { 0, -1 },
13281     { -1, 0 },
13282     { +1, 0 },
13283     { 0, +1 }
13284   };
13285
13286   for (i = 0; i < NUM_DIRECTIONS; i++)
13287   {
13288     int x, y, element;
13289
13290     x = bad_x + xy[i][0];
13291     y = bad_y + xy[i][1];
13292     if (!IN_LEV_FIELD(x, y))
13293       continue;
13294
13295     element = Feld[x][y];
13296     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13297         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13298     {
13299       kill_x = x;
13300       kill_y = y;
13301       break;
13302     }
13303   }
13304
13305   if (kill_x != bad_x || kill_y != bad_y)
13306     Bang(bad_x, bad_y);
13307 }
13308
13309 void KillPlayer(struct PlayerInfo *player)
13310 {
13311   int jx = player->jx, jy = player->jy;
13312
13313   if (!player->active)
13314     return;
13315
13316 #if 0
13317   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13318          player->killed, player->active, player->reanimated);
13319 #endif
13320
13321   /* the following code was introduced to prevent an infinite loop when calling
13322      -> Bang()
13323      -> CheckTriggeredElementChangeExt()
13324      -> ExecuteCustomElementAction()
13325      -> KillPlayer()
13326      -> (infinitely repeating the above sequence of function calls)
13327      which occurs when killing the player while having a CE with the setting
13328      "kill player X when explosion of <player X>"; the solution using a new
13329      field "player->killed" was chosen for backwards compatibility, although
13330      clever use of the fields "player->active" etc. would probably also work */
13331 #if 1
13332   if (player->killed)
13333     return;
13334 #endif
13335
13336   player->killed = TRUE;
13337
13338   // remove accessible field at the player's position
13339   Feld[jx][jy] = EL_EMPTY;
13340
13341   // deactivate shield (else Bang()/Explode() would not work right)
13342   player->shield_normal_time_left = 0;
13343   player->shield_deadly_time_left = 0;
13344
13345 #if 0
13346   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13347          player->killed, player->active, player->reanimated);
13348 #endif
13349
13350   Bang(jx, jy);
13351
13352 #if 0
13353   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13354          player->killed, player->active, player->reanimated);
13355 #endif
13356
13357   if (player->reanimated)       // killed player may have been reanimated
13358     player->killed = player->reanimated = FALSE;
13359   else
13360     BuryPlayer(player);
13361 }
13362
13363 static void KillPlayerUnlessEnemyProtected(int x, int y)
13364 {
13365   if (!PLAYER_ENEMY_PROTECTED(x, y))
13366     KillPlayer(PLAYERINFO(x, y));
13367 }
13368
13369 static void KillPlayerUnlessExplosionProtected(int x, int y)
13370 {
13371   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13372     KillPlayer(PLAYERINFO(x, y));
13373 }
13374
13375 void BuryPlayer(struct PlayerInfo *player)
13376 {
13377   int jx = player->jx, jy = player->jy;
13378
13379   if (!player->active)
13380     return;
13381
13382   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13383   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13384
13385   player->GameOver = TRUE;
13386   RemovePlayer(player);
13387 }
13388
13389 void RemovePlayer(struct PlayerInfo *player)
13390 {
13391   int jx = player->jx, jy = player->jy;
13392   int i, found = FALSE;
13393
13394   player->present = FALSE;
13395   player->active = FALSE;
13396
13397   if (!ExplodeField[jx][jy])
13398     StorePlayer[jx][jy] = 0;
13399
13400   if (player->is_moving)
13401     TEST_DrawLevelField(player->last_jx, player->last_jy);
13402
13403   for (i = 0; i < MAX_PLAYERS; i++)
13404     if (stored_player[i].active)
13405       found = TRUE;
13406
13407   if (!found)
13408     AllPlayersGone = TRUE;
13409
13410   ExitX = ZX = jx;
13411   ExitY = ZY = jy;
13412 }
13413
13414 void ExitPlayer(struct PlayerInfo *player)
13415 {
13416   DrawPlayer(player);   // needed here only to cleanup last field
13417   RemovePlayer(player);
13418
13419   if (local_player->players_still_needed > 0)
13420     local_player->players_still_needed--;
13421
13422   // also set if some players not yet gone, but not needed to solve level
13423   if (local_player->players_still_needed == 0)
13424     AllPlayersGone = TRUE;
13425 }
13426
13427 static void setFieldForSnapping(int x, int y, int element, int direction)
13428 {
13429   struct ElementInfo *ei = &element_info[element];
13430   int direction_bit = MV_DIR_TO_BIT(direction);
13431   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13432   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13433                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13434
13435   Feld[x][y] = EL_ELEMENT_SNAPPING;
13436   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13437
13438   ResetGfxAnimation(x, y);
13439
13440   GfxElement[x][y] = element;
13441   GfxAction[x][y] = action;
13442   GfxDir[x][y] = direction;
13443   GfxFrame[x][y] = -1;
13444 }
13445
13446 /*
13447   =============================================================================
13448   checkDiagonalPushing()
13449   -----------------------------------------------------------------------------
13450   check if diagonal input device direction results in pushing of object
13451   (by checking if the alternative direction is walkable, diggable, ...)
13452   =============================================================================
13453 */
13454
13455 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13456                                     int x, int y, int real_dx, int real_dy)
13457 {
13458   int jx, jy, dx, dy, xx, yy;
13459
13460   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13461     return TRUE;
13462
13463   // diagonal direction: check alternative direction
13464   jx = player->jx;
13465   jy = player->jy;
13466   dx = x - jx;
13467   dy = y - jy;
13468   xx = jx + (dx == 0 ? real_dx : 0);
13469   yy = jy + (dy == 0 ? real_dy : 0);
13470
13471   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13472 }
13473
13474 /*
13475   =============================================================================
13476   DigField()
13477   -----------------------------------------------------------------------------
13478   x, y:                 field next to player (non-diagonal) to try to dig to
13479   real_dx, real_dy:     direction as read from input device (can be diagonal)
13480   =============================================================================
13481 */
13482
13483 static int DigField(struct PlayerInfo *player,
13484                     int oldx, int oldy, int x, int y,
13485                     int real_dx, int real_dy, int mode)
13486 {
13487   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13488   boolean player_was_pushing = player->is_pushing;
13489   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13490   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13491   int jx = oldx, jy = oldy;
13492   int dx = x - jx, dy = y - jy;
13493   int nextx = x + dx, nexty = y + dy;
13494   int move_direction = (dx == -1 ? MV_LEFT  :
13495                         dx == +1 ? MV_RIGHT :
13496                         dy == -1 ? MV_UP    :
13497                         dy == +1 ? MV_DOWN  : MV_NONE);
13498   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13499   int dig_side = MV_DIR_OPPOSITE(move_direction);
13500   int old_element = Feld[jx][jy];
13501   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13502   int collect_count;
13503
13504   if (is_player)                // function can also be called by EL_PENGUIN
13505   {
13506     if (player->MovPos == 0)
13507     {
13508       player->is_digging = FALSE;
13509       player->is_collecting = FALSE;
13510     }
13511
13512     if (player->MovPos == 0)    // last pushing move finished
13513       player->is_pushing = FALSE;
13514
13515     if (mode == DF_NO_PUSH)     // player just stopped pushing
13516     {
13517       player->is_switching = FALSE;
13518       player->push_delay = -1;
13519
13520       return MP_NO_ACTION;
13521     }
13522   }
13523
13524   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13525     old_element = Back[jx][jy];
13526
13527   // in case of element dropped at player position, check background
13528   else if (Back[jx][jy] != EL_EMPTY &&
13529            game.engine_version >= VERSION_IDENT(2,2,0,0))
13530     old_element = Back[jx][jy];
13531
13532   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13533     return MP_NO_ACTION;        // field has no opening in this direction
13534
13535   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13536     return MP_NO_ACTION;        // field has no opening in this direction
13537
13538   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13539   {
13540     SplashAcid(x, y);
13541
13542     Feld[jx][jy] = player->artwork_element;
13543     InitMovingField(jx, jy, MV_DOWN);
13544     Store[jx][jy] = EL_ACID;
13545     ContinueMoving(jx, jy);
13546     BuryPlayer(player);
13547
13548     return MP_DONT_RUN_INTO;
13549   }
13550
13551   if (player_can_move && DONT_RUN_INTO(element))
13552   {
13553     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13554
13555     return MP_DONT_RUN_INTO;
13556   }
13557
13558   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13559     return MP_NO_ACTION;
13560
13561   collect_count = element_info[element].collect_count_initial;
13562
13563   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13564     return MP_NO_ACTION;
13565
13566   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13567     player_can_move = player_can_move_or_snap;
13568
13569   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13570       game.engine_version >= VERSION_IDENT(2,2,0,0))
13571   {
13572     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13573                                player->index_bit, dig_side);
13574     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13575                                         player->index_bit, dig_side);
13576
13577     if (element == EL_DC_LANDMINE)
13578       Bang(x, y);
13579
13580     if (Feld[x][y] != element)          // field changed by snapping
13581       return MP_ACTION;
13582
13583     return MP_NO_ACTION;
13584   }
13585
13586   if (player->gravity && is_player && !player->is_auto_moving &&
13587       canFallDown(player) && move_direction != MV_DOWN &&
13588       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13589     return MP_NO_ACTION;        // player cannot walk here due to gravity
13590
13591   if (player_can_move &&
13592       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13593   {
13594     int sound_element = SND_ELEMENT(element);
13595     int sound_action = ACTION_WALKING;
13596
13597     if (IS_RND_GATE(element))
13598     {
13599       if (!player->key[RND_GATE_NR(element)])
13600         return MP_NO_ACTION;
13601     }
13602     else if (IS_RND_GATE_GRAY(element))
13603     {
13604       if (!player->key[RND_GATE_GRAY_NR(element)])
13605         return MP_NO_ACTION;
13606     }
13607     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13608     {
13609       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13610         return MP_NO_ACTION;
13611     }
13612     else if (element == EL_EXIT_OPEN ||
13613              element == EL_EM_EXIT_OPEN ||
13614              element == EL_EM_EXIT_OPENING ||
13615              element == EL_STEEL_EXIT_OPEN ||
13616              element == EL_EM_STEEL_EXIT_OPEN ||
13617              element == EL_EM_STEEL_EXIT_OPENING ||
13618              element == EL_SP_EXIT_OPEN ||
13619              element == EL_SP_EXIT_OPENING)
13620     {
13621       sound_action = ACTION_PASSING;    // player is passing exit
13622     }
13623     else if (element == EL_EMPTY)
13624     {
13625       sound_action = ACTION_MOVING;             // nothing to walk on
13626     }
13627
13628     // play sound from background or player, whatever is available
13629     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13630       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13631     else
13632       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13633   }
13634   else if (player_can_move &&
13635            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13636   {
13637     if (!ACCESS_FROM(element, opposite_direction))
13638       return MP_NO_ACTION;      // field not accessible from this direction
13639
13640     if (CAN_MOVE(element))      // only fixed elements can be passed!
13641       return MP_NO_ACTION;
13642
13643     if (IS_EM_GATE(element))
13644     {
13645       if (!player->key[EM_GATE_NR(element)])
13646         return MP_NO_ACTION;
13647     }
13648     else if (IS_EM_GATE_GRAY(element))
13649     {
13650       if (!player->key[EM_GATE_GRAY_NR(element)])
13651         return MP_NO_ACTION;
13652     }
13653     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13654     {
13655       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13656         return MP_NO_ACTION;
13657     }
13658     else if (IS_EMC_GATE(element))
13659     {
13660       if (!player->key[EMC_GATE_NR(element)])
13661         return MP_NO_ACTION;
13662     }
13663     else if (IS_EMC_GATE_GRAY(element))
13664     {
13665       if (!player->key[EMC_GATE_GRAY_NR(element)])
13666         return MP_NO_ACTION;
13667     }
13668     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13669     {
13670       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13671         return MP_NO_ACTION;
13672     }
13673     else if (element == EL_DC_GATE_WHITE ||
13674              element == EL_DC_GATE_WHITE_GRAY ||
13675              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13676     {
13677       if (player->num_white_keys == 0)
13678         return MP_NO_ACTION;
13679
13680       player->num_white_keys--;
13681     }
13682     else if (IS_SP_PORT(element))
13683     {
13684       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13685           element == EL_SP_GRAVITY_PORT_RIGHT ||
13686           element == EL_SP_GRAVITY_PORT_UP ||
13687           element == EL_SP_GRAVITY_PORT_DOWN)
13688         player->gravity = !player->gravity;
13689       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13690                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13691                element == EL_SP_GRAVITY_ON_PORT_UP ||
13692                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13693         player->gravity = TRUE;
13694       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13695                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13696                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13697                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13698         player->gravity = FALSE;
13699     }
13700
13701     // automatically move to the next field with double speed
13702     player->programmed_action = move_direction;
13703
13704     if (player->move_delay_reset_counter == 0)
13705     {
13706       player->move_delay_reset_counter = 2;     // two double speed steps
13707
13708       DOUBLE_PLAYER_SPEED(player);
13709     }
13710
13711     PlayLevelSoundAction(x, y, ACTION_PASSING);
13712   }
13713   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13714   {
13715     RemoveField(x, y);
13716
13717     if (mode != DF_SNAP)
13718     {
13719       GfxElement[x][y] = GFX_ELEMENT(element);
13720       player->is_digging = TRUE;
13721     }
13722
13723     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13724
13725     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13726                                         player->index_bit, dig_side);
13727
13728     if (mode == DF_SNAP)
13729     {
13730       if (level.block_snap_field)
13731         setFieldForSnapping(x, y, element, move_direction);
13732       else
13733         TestIfElementTouchesCustomElement(x, y);        // for empty space
13734
13735       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13736                                           player->index_bit, dig_side);
13737     }
13738   }
13739   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13740   {
13741     RemoveField(x, y);
13742
13743     if (is_player && mode != DF_SNAP)
13744     {
13745       GfxElement[x][y] = element;
13746       player->is_collecting = TRUE;
13747     }
13748
13749     if (element == EL_SPEED_PILL)
13750     {
13751       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13752     }
13753     else if (element == EL_EXTRA_TIME && level.time > 0)
13754     {
13755       TimeLeft += level.extra_time;
13756
13757       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13758
13759       DisplayGameControlValues();
13760     }
13761     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13762     {
13763       player->shield_normal_time_left += level.shield_normal_time;
13764       if (element == EL_SHIELD_DEADLY)
13765         player->shield_deadly_time_left += level.shield_deadly_time;
13766     }
13767     else if (element == EL_DYNAMITE ||
13768              element == EL_EM_DYNAMITE ||
13769              element == EL_SP_DISK_RED)
13770     {
13771       if (player->inventory_size < MAX_INVENTORY_SIZE)
13772         player->inventory_element[player->inventory_size++] = element;
13773
13774       DrawGameDoorValues();
13775     }
13776     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13777     {
13778       player->dynabomb_count++;
13779       player->dynabombs_left++;
13780     }
13781     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13782     {
13783       player->dynabomb_size++;
13784     }
13785     else if (element == EL_DYNABOMB_INCREASE_POWER)
13786     {
13787       player->dynabomb_xl = TRUE;
13788     }
13789     else if (IS_KEY(element))
13790     {
13791       player->key[KEY_NR(element)] = TRUE;
13792
13793       DrawGameDoorValues();
13794     }
13795     else if (element == EL_DC_KEY_WHITE)
13796     {
13797       player->num_white_keys++;
13798
13799       // display white keys?
13800       // DrawGameDoorValues();
13801     }
13802     else if (IS_ENVELOPE(element))
13803     {
13804       player->show_envelope = element;
13805     }
13806     else if (element == EL_EMC_LENSES)
13807     {
13808       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13809
13810       RedrawAllInvisibleElementsForLenses();
13811     }
13812     else if (element == EL_EMC_MAGNIFIER)
13813     {
13814       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13815
13816       RedrawAllInvisibleElementsForMagnifier();
13817     }
13818     else if (IS_DROPPABLE(element) ||
13819              IS_THROWABLE(element))     // can be collected and dropped
13820     {
13821       int i;
13822
13823       if (collect_count == 0)
13824         player->inventory_infinite_element = element;
13825       else
13826         for (i = 0; i < collect_count; i++)
13827           if (player->inventory_size < MAX_INVENTORY_SIZE)
13828             player->inventory_element[player->inventory_size++] = element;
13829
13830       DrawGameDoorValues();
13831     }
13832     else if (collect_count > 0)
13833     {
13834       local_player->gems_still_needed -= collect_count;
13835       if (local_player->gems_still_needed < 0)
13836         local_player->gems_still_needed = 0;
13837
13838       game.snapshot.collected_item = TRUE;
13839
13840       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13841
13842       DisplayGameControlValues();
13843     }
13844
13845     RaiseScoreElement(element);
13846     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13847
13848     if (is_player)
13849       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13850                                           player->index_bit, dig_side);
13851
13852     if (mode == DF_SNAP)
13853     {
13854       if (level.block_snap_field)
13855         setFieldForSnapping(x, y, element, move_direction);
13856       else
13857         TestIfElementTouchesCustomElement(x, y);        // for empty space
13858
13859       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13860                                           player->index_bit, dig_side);
13861     }
13862   }
13863   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13864   {
13865     if (mode == DF_SNAP && element != EL_BD_ROCK)
13866       return MP_NO_ACTION;
13867
13868     if (CAN_FALL(element) && dy)
13869       return MP_NO_ACTION;
13870
13871     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13872         !(element == EL_SPRING && level.use_spring_bug))
13873       return MP_NO_ACTION;
13874
13875     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13876         ((move_direction & MV_VERTICAL &&
13877           ((element_info[element].move_pattern & MV_LEFT &&
13878             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13879            (element_info[element].move_pattern & MV_RIGHT &&
13880             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13881          (move_direction & MV_HORIZONTAL &&
13882           ((element_info[element].move_pattern & MV_UP &&
13883             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13884            (element_info[element].move_pattern & MV_DOWN &&
13885             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13886       return MP_NO_ACTION;
13887
13888     // do not push elements already moving away faster than player
13889     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13890         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13891       return MP_NO_ACTION;
13892
13893     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13894     {
13895       if (player->push_delay_value == -1 || !player_was_pushing)
13896         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13897     }
13898     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13899     {
13900       if (player->push_delay_value == -1)
13901         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13902     }
13903     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13904     {
13905       if (!player->is_pushing)
13906         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13907     }
13908
13909     player->is_pushing = TRUE;
13910     player->is_active = TRUE;
13911
13912     if (!(IN_LEV_FIELD(nextx, nexty) &&
13913           (IS_FREE(nextx, nexty) ||
13914            (IS_SB_ELEMENT(element) &&
13915             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13916            (IS_CUSTOM_ELEMENT(element) &&
13917             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13918       return MP_NO_ACTION;
13919
13920     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13921       return MP_NO_ACTION;
13922
13923     if (player->push_delay == -1)       // new pushing; restart delay
13924       player->push_delay = 0;
13925
13926     if (player->push_delay < player->push_delay_value &&
13927         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13928         element != EL_SPRING && element != EL_BALLOON)
13929     {
13930       // make sure that there is no move delay before next try to push
13931       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13932         player->move_delay = 0;
13933
13934       return MP_NO_ACTION;
13935     }
13936
13937     if (IS_CUSTOM_ELEMENT(element) &&
13938         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13939     {
13940       if (!DigFieldByCE(nextx, nexty, element))
13941         return MP_NO_ACTION;
13942     }
13943
13944     if (IS_SB_ELEMENT(element))
13945     {
13946       boolean sokoban_task_solved = FALSE;
13947
13948       if (element == EL_SOKOBAN_FIELD_FULL)
13949       {
13950         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13951         local_player->sokoban_fields_still_needed++;
13952         local_player->sokoban_objects_still_needed++;
13953       }
13954
13955       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13956       {
13957         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13958         local_player->sokoban_fields_still_needed--;
13959         local_player->sokoban_objects_still_needed--;
13960
13961         // sokoban object was pushed from empty field to sokoban field
13962         if (Back[x][y] == EL_EMPTY)
13963           sokoban_task_solved = TRUE;
13964       }
13965
13966       Feld[x][y] = EL_SOKOBAN_OBJECT;
13967
13968       if (Back[x][y] == Back[nextx][nexty])
13969         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13970       else if (Back[x][y] != 0)
13971         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13972                                     ACTION_EMPTYING);
13973       else
13974         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13975                                     ACTION_FILLING);
13976
13977       if (sokoban_task_solved &&
13978           local_player->sokoban_fields_still_needed == 0 &&
13979           local_player->sokoban_objects_still_needed == 0 &&
13980           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13981       {
13982         local_player->players_still_needed = 0;
13983
13984         PlayerWins(local_player);
13985
13986         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13987       }
13988     }
13989     else
13990       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13991
13992     InitMovingField(x, y, move_direction);
13993     GfxAction[x][y] = ACTION_PUSHING;
13994
13995     if (mode == DF_SNAP)
13996       ContinueMoving(x, y);
13997     else
13998       MovPos[x][y] = (dx != 0 ? dx : dy);
13999
14000     Pushed[x][y] = TRUE;
14001     Pushed[nextx][nexty] = TRUE;
14002
14003     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14004       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14005     else
14006       player->push_delay_value = -1;    // get new value later
14007
14008     // check for element change _after_ element has been pushed
14009     if (game.use_change_when_pushing_bug)
14010     {
14011       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14012                                  player->index_bit, dig_side);
14013       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14014                                           player->index_bit, dig_side);
14015     }
14016   }
14017   else if (IS_SWITCHABLE(element))
14018   {
14019     if (PLAYER_SWITCHING(player, x, y))
14020     {
14021       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14022                                           player->index_bit, dig_side);
14023
14024       return MP_ACTION;
14025     }
14026
14027     player->is_switching = TRUE;
14028     player->switch_x = x;
14029     player->switch_y = y;
14030
14031     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14032
14033     if (element == EL_ROBOT_WHEEL)
14034     {
14035       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14036       ZX = x;
14037       ZY = y;
14038
14039       game.robot_wheel_active = TRUE;
14040
14041       TEST_DrawLevelField(x, y);
14042     }
14043     else if (element == EL_SP_TERMINAL)
14044     {
14045       int xx, yy;
14046
14047       SCAN_PLAYFIELD(xx, yy)
14048       {
14049         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14050         {
14051           Bang(xx, yy);
14052         }
14053         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14054         {
14055           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14056
14057           ResetGfxAnimation(xx, yy);
14058           TEST_DrawLevelField(xx, yy);
14059         }
14060       }
14061     }
14062     else if (IS_BELT_SWITCH(element))
14063     {
14064       ToggleBeltSwitch(x, y);
14065     }
14066     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14067              element == EL_SWITCHGATE_SWITCH_DOWN ||
14068              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14069              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14070     {
14071       ToggleSwitchgateSwitch(x, y);
14072     }
14073     else if (element == EL_LIGHT_SWITCH ||
14074              element == EL_LIGHT_SWITCH_ACTIVE)
14075     {
14076       ToggleLightSwitch(x, y);
14077     }
14078     else if (element == EL_TIMEGATE_SWITCH ||
14079              element == EL_DC_TIMEGATE_SWITCH)
14080     {
14081       ActivateTimegateSwitch(x, y);
14082     }
14083     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14084              element == EL_BALLOON_SWITCH_RIGHT ||
14085              element == EL_BALLOON_SWITCH_UP    ||
14086              element == EL_BALLOON_SWITCH_DOWN  ||
14087              element == EL_BALLOON_SWITCH_NONE  ||
14088              element == EL_BALLOON_SWITCH_ANY)
14089     {
14090       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14091                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14092                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14093                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14094                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14095                              move_direction);
14096     }
14097     else if (element == EL_LAMP)
14098     {
14099       Feld[x][y] = EL_LAMP_ACTIVE;
14100       local_player->lights_still_needed--;
14101
14102       ResetGfxAnimation(x, y);
14103       TEST_DrawLevelField(x, y);
14104     }
14105     else if (element == EL_TIME_ORB_FULL)
14106     {
14107       Feld[x][y] = EL_TIME_ORB_EMPTY;
14108
14109       if (level.time > 0 || level.use_time_orb_bug)
14110       {
14111         TimeLeft += level.time_orb_time;
14112         game.no_time_limit = FALSE;
14113
14114         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14115
14116         DisplayGameControlValues();
14117       }
14118
14119       ResetGfxAnimation(x, y);
14120       TEST_DrawLevelField(x, y);
14121     }
14122     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14123              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14124     {
14125       int xx, yy;
14126
14127       game.ball_state = !game.ball_state;
14128
14129       SCAN_PLAYFIELD(xx, yy)
14130       {
14131         int e = Feld[xx][yy];
14132
14133         if (game.ball_state)
14134         {
14135           if (e == EL_EMC_MAGIC_BALL)
14136             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14137           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14138             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14139         }
14140         else
14141         {
14142           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14143             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14144           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14145             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14146         }
14147       }
14148     }
14149
14150     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14151                                         player->index_bit, dig_side);
14152
14153     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14154                                         player->index_bit, dig_side);
14155
14156     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14157                                         player->index_bit, dig_side);
14158
14159     return MP_ACTION;
14160   }
14161   else
14162   {
14163     if (!PLAYER_SWITCHING(player, x, y))
14164     {
14165       player->is_switching = TRUE;
14166       player->switch_x = x;
14167       player->switch_y = y;
14168
14169       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14170                                  player->index_bit, dig_side);
14171       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14172                                           player->index_bit, dig_side);
14173
14174       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14175                                  player->index_bit, dig_side);
14176       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14177                                           player->index_bit, dig_side);
14178     }
14179
14180     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14181                                player->index_bit, dig_side);
14182     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14183                                         player->index_bit, dig_side);
14184
14185     return MP_NO_ACTION;
14186   }
14187
14188   player->push_delay = -1;
14189
14190   if (is_player)                // function can also be called by EL_PENGUIN
14191   {
14192     if (Feld[x][y] != element)          // really digged/collected something
14193     {
14194       player->is_collecting = !player->is_digging;
14195       player->is_active = TRUE;
14196     }
14197   }
14198
14199   return MP_MOVING;
14200 }
14201
14202 static boolean DigFieldByCE(int x, int y, int digging_element)
14203 {
14204   int element = Feld[x][y];
14205
14206   if (!IS_FREE(x, y))
14207   {
14208     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14209                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14210                   ACTION_BREAKING);
14211
14212     // no element can dig solid indestructible elements
14213     if (IS_INDESTRUCTIBLE(element) &&
14214         !IS_DIGGABLE(element) &&
14215         !IS_COLLECTIBLE(element))
14216       return FALSE;
14217
14218     if (AmoebaNr[x][y] &&
14219         (element == EL_AMOEBA_FULL ||
14220          element == EL_BD_AMOEBA ||
14221          element == EL_AMOEBA_GROWING))
14222     {
14223       AmoebaCnt[AmoebaNr[x][y]]--;
14224       AmoebaCnt2[AmoebaNr[x][y]]--;
14225     }
14226
14227     if (IS_MOVING(x, y))
14228       RemoveMovingField(x, y);
14229     else
14230     {
14231       RemoveField(x, y);
14232       TEST_DrawLevelField(x, y);
14233     }
14234
14235     // if digged element was about to explode, prevent the explosion
14236     ExplodeField[x][y] = EX_TYPE_NONE;
14237
14238     PlayLevelSoundAction(x, y, action);
14239   }
14240
14241   Store[x][y] = EL_EMPTY;
14242
14243   // this makes it possible to leave the removed element again
14244   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14245     Store[x][y] = element;
14246
14247   return TRUE;
14248 }
14249
14250 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14251 {
14252   int jx = player->jx, jy = player->jy;
14253   int x = jx + dx, y = jy + dy;
14254   int snap_direction = (dx == -1 ? MV_LEFT  :
14255                         dx == +1 ? MV_RIGHT :
14256                         dy == -1 ? MV_UP    :
14257                         dy == +1 ? MV_DOWN  : MV_NONE);
14258   boolean can_continue_snapping = (level.continuous_snapping &&
14259                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14260
14261   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14262     return FALSE;
14263
14264   if (!player->active || !IN_LEV_FIELD(x, y))
14265     return FALSE;
14266
14267   if (dx && dy)
14268     return FALSE;
14269
14270   if (!dx && !dy)
14271   {
14272     if (player->MovPos == 0)
14273       player->is_pushing = FALSE;
14274
14275     player->is_snapping = FALSE;
14276
14277     if (player->MovPos == 0)
14278     {
14279       player->is_moving = FALSE;
14280       player->is_digging = FALSE;
14281       player->is_collecting = FALSE;
14282     }
14283
14284     return FALSE;
14285   }
14286
14287   // prevent snapping with already pressed snap key when not allowed
14288   if (player->is_snapping && !can_continue_snapping)
14289     return FALSE;
14290
14291   player->MovDir = snap_direction;
14292
14293   if (player->MovPos == 0)
14294   {
14295     player->is_moving = FALSE;
14296     player->is_digging = FALSE;
14297     player->is_collecting = FALSE;
14298   }
14299
14300   player->is_dropping = FALSE;
14301   player->is_dropping_pressed = FALSE;
14302   player->drop_pressed_delay = 0;
14303
14304   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14305     return FALSE;
14306
14307   player->is_snapping = TRUE;
14308   player->is_active = TRUE;
14309
14310   if (player->MovPos == 0)
14311   {
14312     player->is_moving = FALSE;
14313     player->is_digging = FALSE;
14314     player->is_collecting = FALSE;
14315   }
14316
14317   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14318     TEST_DrawLevelField(player->last_jx, player->last_jy);
14319
14320   TEST_DrawLevelField(x, y);
14321
14322   return TRUE;
14323 }
14324
14325 static boolean DropElement(struct PlayerInfo *player)
14326 {
14327   int old_element, new_element;
14328   int dropx = player->jx, dropy = player->jy;
14329   int drop_direction = player->MovDir;
14330   int drop_side = drop_direction;
14331   int drop_element = get_next_dropped_element(player);
14332
14333   /* do not drop an element on top of another element; when holding drop key
14334      pressed without moving, dropped element must move away before the next
14335      element can be dropped (this is especially important if the next element
14336      is dynamite, which can be placed on background for historical reasons) */
14337   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14338     return MP_ACTION;
14339
14340   if (IS_THROWABLE(drop_element))
14341   {
14342     dropx += GET_DX_FROM_DIR(drop_direction);
14343     dropy += GET_DY_FROM_DIR(drop_direction);
14344
14345     if (!IN_LEV_FIELD(dropx, dropy))
14346       return FALSE;
14347   }
14348
14349   old_element = Feld[dropx][dropy];     // old element at dropping position
14350   new_element = drop_element;           // default: no change when dropping
14351
14352   // check if player is active, not moving and ready to drop
14353   if (!player->active || player->MovPos || player->drop_delay > 0)
14354     return FALSE;
14355
14356   // check if player has anything that can be dropped
14357   if (new_element == EL_UNDEFINED)
14358     return FALSE;
14359
14360   // only set if player has anything that can be dropped
14361   player->is_dropping_pressed = TRUE;
14362
14363   // check if drop key was pressed long enough for EM style dynamite
14364   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14365     return FALSE;
14366
14367   // check if anything can be dropped at the current position
14368   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14369     return FALSE;
14370
14371   // collected custom elements can only be dropped on empty fields
14372   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14373     return FALSE;
14374
14375   if (old_element != EL_EMPTY)
14376     Back[dropx][dropy] = old_element;   // store old element on this field
14377
14378   ResetGfxAnimation(dropx, dropy);
14379   ResetRandomAnimationValue(dropx, dropy);
14380
14381   if (player->inventory_size > 0 ||
14382       player->inventory_infinite_element != EL_UNDEFINED)
14383   {
14384     if (player->inventory_size > 0)
14385     {
14386       player->inventory_size--;
14387
14388       DrawGameDoorValues();
14389
14390       if (new_element == EL_DYNAMITE)
14391         new_element = EL_DYNAMITE_ACTIVE;
14392       else if (new_element == EL_EM_DYNAMITE)
14393         new_element = EL_EM_DYNAMITE_ACTIVE;
14394       else if (new_element == EL_SP_DISK_RED)
14395         new_element = EL_SP_DISK_RED_ACTIVE;
14396     }
14397
14398     Feld[dropx][dropy] = new_element;
14399
14400     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14401       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14402                           el2img(Feld[dropx][dropy]), 0);
14403
14404     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14405
14406     // needed if previous element just changed to "empty" in the last frame
14407     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14408
14409     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14410                                player->index_bit, drop_side);
14411     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14412                                         CE_PLAYER_DROPS_X,
14413                                         player->index_bit, drop_side);
14414
14415     TestIfElementTouchesCustomElement(dropx, dropy);
14416   }
14417   else          // player is dropping a dyna bomb
14418   {
14419     player->dynabombs_left--;
14420
14421     Feld[dropx][dropy] = new_element;
14422
14423     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14424       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14425                           el2img(Feld[dropx][dropy]), 0);
14426
14427     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14428   }
14429
14430   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14431     InitField_WithBug1(dropx, dropy, FALSE);
14432
14433   new_element = Feld[dropx][dropy];     // element might have changed
14434
14435   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14436       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14437   {
14438     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14439       MovDir[dropx][dropy] = drop_direction;
14440
14441     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14442
14443     // do not cause impact style collision by dropping elements that can fall
14444     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14445   }
14446
14447   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14448   player->is_dropping = TRUE;
14449
14450   player->drop_pressed_delay = 0;
14451   player->is_dropping_pressed = FALSE;
14452
14453   player->drop_x = dropx;
14454   player->drop_y = dropy;
14455
14456   return TRUE;
14457 }
14458
14459 // ----------------------------------------------------------------------------
14460 // game sound playing functions
14461 // ----------------------------------------------------------------------------
14462
14463 static int *loop_sound_frame = NULL;
14464 static int *loop_sound_volume = NULL;
14465
14466 void InitPlayLevelSound(void)
14467 {
14468   int num_sounds = getSoundListSize();
14469
14470   checked_free(loop_sound_frame);
14471   checked_free(loop_sound_volume);
14472
14473   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14474   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14475 }
14476
14477 static void PlayLevelSound(int x, int y, int nr)
14478 {
14479   int sx = SCREENX(x), sy = SCREENY(y);
14480   int volume, stereo_position;
14481   int max_distance = 8;
14482   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14483
14484   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14485       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14486     return;
14487
14488   if (!IN_LEV_FIELD(x, y) ||
14489       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14490       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14491     return;
14492
14493   volume = SOUND_MAX_VOLUME;
14494
14495   if (!IN_SCR_FIELD(sx, sy))
14496   {
14497     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14498     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14499
14500     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14501   }
14502
14503   stereo_position = (SOUND_MAX_LEFT +
14504                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14505                      (SCR_FIELDX + 2 * max_distance));
14506
14507   if (IS_LOOP_SOUND(nr))
14508   {
14509     /* This assures that quieter loop sounds do not overwrite louder ones,
14510        while restarting sound volume comparison with each new game frame. */
14511
14512     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14513       return;
14514
14515     loop_sound_volume[nr] = volume;
14516     loop_sound_frame[nr] = FrameCounter;
14517   }
14518
14519   PlaySoundExt(nr, volume, stereo_position, type);
14520 }
14521
14522 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14523 {
14524   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14525                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14526                  y < LEVELY(BY1) ? LEVELY(BY1) :
14527                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14528                  sound_action);
14529 }
14530
14531 static void PlayLevelSoundAction(int x, int y, int action)
14532 {
14533   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14534 }
14535
14536 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14537 {
14538   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14539
14540   if (sound_effect != SND_UNDEFINED)
14541     PlayLevelSound(x, y, sound_effect);
14542 }
14543
14544 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14545                                               int action)
14546 {
14547   int sound_effect = element_info[SND_ELEMENT(element)].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 PlayLevelSoundActionIfLoop(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     PlayLevelSound(x, y, sound_effect);
14559 }
14560
14561 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14562 {
14563   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14564
14565   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14566     StopSound(sound_effect);
14567 }
14568
14569 static int getLevelMusicNr(void)
14570 {
14571   if (levelset.music[level_nr] != MUS_UNDEFINED)
14572     return levelset.music[level_nr];            // from config file
14573   else
14574     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14575 }
14576
14577 static void FadeLevelSounds(void)
14578 {
14579   FadeSounds();
14580 }
14581
14582 static void FadeLevelMusic(void)
14583 {
14584   int music_nr = getLevelMusicNr();
14585   char *curr_music = getCurrentlyPlayingMusicFilename();
14586   char *next_music = getMusicInfoEntryFilename(music_nr);
14587
14588   if (!strEqual(curr_music, next_music))
14589     FadeMusic();
14590 }
14591
14592 void FadeLevelSoundsAndMusic(void)
14593 {
14594   FadeLevelSounds();
14595   FadeLevelMusic();
14596 }
14597
14598 static void PlayLevelMusic(void)
14599 {
14600   int music_nr = getLevelMusicNr();
14601   char *curr_music = getCurrentlyPlayingMusicFilename();
14602   char *next_music = getMusicInfoEntryFilename(music_nr);
14603
14604   if (!strEqual(curr_music, next_music))
14605     PlayMusicLoop(music_nr);
14606 }
14607
14608 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14609 {
14610   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14611   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14612   int x = xx - 1 - offset;
14613   int y = yy - 1 - offset;
14614
14615   switch (sample)
14616   {
14617     case SAMPLE_blank:
14618       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14619       break;
14620
14621     case SAMPLE_roll:
14622       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14623       break;
14624
14625     case SAMPLE_stone:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14627       break;
14628
14629     case SAMPLE_nut:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14631       break;
14632
14633     case SAMPLE_crack:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14635       break;
14636
14637     case SAMPLE_bug:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14639       break;
14640
14641     case SAMPLE_tank:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14643       break;
14644
14645     case SAMPLE_android_clone:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14647       break;
14648
14649     case SAMPLE_android_move:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14651       break;
14652
14653     case SAMPLE_spring:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14655       break;
14656
14657     case SAMPLE_slurp:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14659       break;
14660
14661     case SAMPLE_eater:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14663       break;
14664
14665     case SAMPLE_eater_eat:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14667       break;
14668
14669     case SAMPLE_alien:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14671       break;
14672
14673     case SAMPLE_collect:
14674       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14675       break;
14676
14677     case SAMPLE_diamond:
14678       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14679       break;
14680
14681     case SAMPLE_squash:
14682       // !!! CHECK THIS !!!
14683 #if 1
14684       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14685 #else
14686       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14687 #endif
14688       break;
14689
14690     case SAMPLE_wonderfall:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14692       break;
14693
14694     case SAMPLE_drip:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14696       break;
14697
14698     case SAMPLE_push:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14700       break;
14701
14702     case SAMPLE_dirt:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14704       break;
14705
14706     case SAMPLE_acid:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14708       break;
14709
14710     case SAMPLE_ball:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14712       break;
14713
14714     case SAMPLE_grow:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14716       break;
14717
14718     case SAMPLE_wonder:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14720       break;
14721
14722     case SAMPLE_door:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14724       break;
14725
14726     case SAMPLE_exit_open:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14728       break;
14729
14730     case SAMPLE_exit_leave:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14732       break;
14733
14734     case SAMPLE_dynamite:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14736       break;
14737
14738     case SAMPLE_tick:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14740       break;
14741
14742     case SAMPLE_press:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14744       break;
14745
14746     case SAMPLE_wheel:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14748       break;
14749
14750     case SAMPLE_boom:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14752       break;
14753
14754     case SAMPLE_die:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14756       break;
14757
14758     case SAMPLE_time:
14759       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14760       break;
14761
14762     default:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14764       break;
14765   }
14766 }
14767
14768 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14769 {
14770   int element = map_element_SP_to_RND(element_sp);
14771   int action = map_action_SP_to_RND(action_sp);
14772   int offset = (setup.sp_show_border_elements ? 0 : 1);
14773   int x = xx - offset;
14774   int y = yy - offset;
14775
14776   PlayLevelSoundElementAction(x, y, element, action);
14777 }
14778
14779 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14780 {
14781   int element = map_element_MM_to_RND(element_mm);
14782   int action = map_action_MM_to_RND(action_mm);
14783   int offset = 0;
14784   int x = xx - offset;
14785   int y = yy - offset;
14786
14787   if (!IS_MM_ELEMENT(element))
14788     element = EL_MM_DEFAULT;
14789
14790   PlayLevelSoundElementAction(x, y, element, action);
14791 }
14792
14793 void PlaySound_MM(int sound_mm)
14794 {
14795   int sound = map_sound_MM_to_RND(sound_mm);
14796
14797   if (sound == SND_UNDEFINED)
14798     return;
14799
14800   PlaySound(sound);
14801 }
14802
14803 void PlaySoundLoop_MM(int sound_mm)
14804 {
14805   int sound = map_sound_MM_to_RND(sound_mm);
14806
14807   if (sound == SND_UNDEFINED)
14808     return;
14809
14810   PlaySoundLoop(sound);
14811 }
14812
14813 void StopSound_MM(int sound_mm)
14814 {
14815   int sound = map_sound_MM_to_RND(sound_mm);
14816
14817   if (sound == SND_UNDEFINED)
14818     return;
14819
14820   StopSound(sound);
14821 }
14822
14823 void RaiseScore(int value)
14824 {
14825   local_player->score += value;
14826
14827   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14828
14829   DisplayGameControlValues();
14830 }
14831
14832 void RaiseScoreElement(int element)
14833 {
14834   switch (element)
14835   {
14836     case EL_EMERALD:
14837     case EL_BD_DIAMOND:
14838     case EL_EMERALD_YELLOW:
14839     case EL_EMERALD_RED:
14840     case EL_EMERALD_PURPLE:
14841     case EL_SP_INFOTRON:
14842       RaiseScore(level.score[SC_EMERALD]);
14843       break;
14844     case EL_DIAMOND:
14845       RaiseScore(level.score[SC_DIAMOND]);
14846       break;
14847     case EL_CRYSTAL:
14848       RaiseScore(level.score[SC_CRYSTAL]);
14849       break;
14850     case EL_PEARL:
14851       RaiseScore(level.score[SC_PEARL]);
14852       break;
14853     case EL_BUG:
14854     case EL_BD_BUTTERFLY:
14855     case EL_SP_ELECTRON:
14856       RaiseScore(level.score[SC_BUG]);
14857       break;
14858     case EL_SPACESHIP:
14859     case EL_BD_FIREFLY:
14860     case EL_SP_SNIKSNAK:
14861       RaiseScore(level.score[SC_SPACESHIP]);
14862       break;
14863     case EL_YAMYAM:
14864     case EL_DARK_YAMYAM:
14865       RaiseScore(level.score[SC_YAMYAM]);
14866       break;
14867     case EL_ROBOT:
14868       RaiseScore(level.score[SC_ROBOT]);
14869       break;
14870     case EL_PACMAN:
14871       RaiseScore(level.score[SC_PACMAN]);
14872       break;
14873     case EL_NUT:
14874       RaiseScore(level.score[SC_NUT]);
14875       break;
14876     case EL_DYNAMITE:
14877     case EL_EM_DYNAMITE:
14878     case EL_SP_DISK_RED:
14879     case EL_DYNABOMB_INCREASE_NUMBER:
14880     case EL_DYNABOMB_INCREASE_SIZE:
14881     case EL_DYNABOMB_INCREASE_POWER:
14882       RaiseScore(level.score[SC_DYNAMITE]);
14883       break;
14884     case EL_SHIELD_NORMAL:
14885     case EL_SHIELD_DEADLY:
14886       RaiseScore(level.score[SC_SHIELD]);
14887       break;
14888     case EL_EXTRA_TIME:
14889       RaiseScore(level.extra_time_score);
14890       break;
14891     case EL_KEY_1:
14892     case EL_KEY_2:
14893     case EL_KEY_3:
14894     case EL_KEY_4:
14895     case EL_EM_KEY_1:
14896     case EL_EM_KEY_2:
14897     case EL_EM_KEY_3:
14898     case EL_EM_KEY_4:
14899     case EL_EMC_KEY_5:
14900     case EL_EMC_KEY_6:
14901     case EL_EMC_KEY_7:
14902     case EL_EMC_KEY_8:
14903     case EL_DC_KEY_WHITE:
14904       RaiseScore(level.score[SC_KEY]);
14905       break;
14906     default:
14907       RaiseScore(element_info[element].collect_score);
14908       break;
14909   }
14910 }
14911
14912 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14913 {
14914   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14915   {
14916     // closing door required in case of envelope style request dialogs
14917     if (!skip_request)
14918       CloseDoor(DOOR_CLOSE_1);
14919
14920     if (network.enabled)
14921       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14922     else
14923     {
14924       if (quick_quit)
14925         FadeSkipNextFadeIn();
14926
14927       SetGameStatus(GAME_MODE_MAIN);
14928
14929       DrawMainMenu();
14930     }
14931   }
14932   else          // continue playing the game
14933   {
14934     if (tape.playing && tape.deactivate_display)
14935       TapeDeactivateDisplayOff(TRUE);
14936
14937     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14938
14939     if (tape.playing && tape.deactivate_display)
14940       TapeDeactivateDisplayOn();
14941   }
14942 }
14943
14944 void RequestQuitGame(boolean ask_if_really_quit)
14945 {
14946   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14947   boolean skip_request = AllPlayersGone || quick_quit;
14948
14949   RequestQuitGameExt(skip_request, quick_quit,
14950                      "Do you really want to quit the game?");
14951 }
14952
14953 void RequestRestartGame(char *message)
14954 {
14955   game.restart_game_message = NULL;
14956
14957   boolean has_started_game = hasStartedNetworkGame();
14958   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
14959
14960   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
14961   {
14962     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14963   }
14964   else
14965   {
14966     SetGameStatus(GAME_MODE_MAIN);
14967
14968     DrawMainMenu();
14969   }
14970 }
14971
14972 void CheckGameOver(void)
14973 {
14974   static boolean last_game_over = FALSE;
14975   static int game_over_delay = 0;
14976   int game_over_delay_value = 50;
14977   boolean game_over = checkGameFailed();
14978
14979   // do not handle game over if request dialog is already active
14980   if (game.request_active)
14981     return;
14982
14983   if (!game_over)
14984   {
14985     last_game_over = FALSE;
14986     game_over_delay = game_over_delay_value;
14987
14988     return;
14989   }
14990
14991   if (game_over_delay > 0)
14992   {
14993     game_over_delay--;
14994
14995     return;
14996   }
14997
14998   if (last_game_over != game_over)
14999     game.restart_game_message = (hasStartedNetworkGame() ?
15000                                  "Game over! Play it again?" :
15001                                  "Game over!");
15002
15003   last_game_over = game_over;
15004 }
15005
15006 boolean checkGameSolved(void)
15007 {
15008   // set for all game engines if level was solved
15009   return local_player->LevelSolved_GameEnd;
15010 }
15011
15012 boolean checkGameFailed(void)
15013 {
15014   if (!AllPlayersGone)
15015     return FALSE;
15016
15017   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15018     return (level.native_em_level->lev->home > 0);
15019   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15020     return (game_sp.GameOver && !game_sp.LevelSolved);
15021   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15022     return (game_mm.game_over && !game_mm.level_solved);
15023   else                          // GAME_ENGINE_TYPE_RND
15024     return (local_player->GameOver && !local_player->LevelSolved);
15025 }
15026
15027 boolean checkGameEnded(void)
15028 {
15029   return (checkGameSolved() || checkGameFailed());
15030 }
15031
15032
15033 // ----------------------------------------------------------------------------
15034 // random generator functions
15035 // ----------------------------------------------------------------------------
15036
15037 unsigned int InitEngineRandom_RND(int seed)
15038 {
15039   game.num_random_calls = 0;
15040
15041   return InitEngineRandom(seed);
15042 }
15043
15044 unsigned int RND(int max)
15045 {
15046   if (max > 0)
15047   {
15048     game.num_random_calls++;
15049
15050     return GetEngineRandom(max);
15051   }
15052
15053   return 0;
15054 }
15055
15056
15057 // ----------------------------------------------------------------------------
15058 // game engine snapshot handling functions
15059 // ----------------------------------------------------------------------------
15060
15061 struct EngineSnapshotInfo
15062 {
15063   // runtime values for custom element collect score
15064   int collect_score[NUM_CUSTOM_ELEMENTS];
15065
15066   // runtime values for group element choice position
15067   int choice_pos[NUM_GROUP_ELEMENTS];
15068
15069   // runtime values for belt position animations
15070   int belt_graphic[4][NUM_BELT_PARTS];
15071   int belt_anim_mode[4][NUM_BELT_PARTS];
15072 };
15073
15074 static struct EngineSnapshotInfo engine_snapshot_rnd;
15075 static char *snapshot_level_identifier = NULL;
15076 static int snapshot_level_nr = -1;
15077
15078 static void SaveEngineSnapshotValues_RND(void)
15079 {
15080   static int belt_base_active_element[4] =
15081   {
15082     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15083     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15084     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15085     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15086   };
15087   int i, j;
15088
15089   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15090   {
15091     int element = EL_CUSTOM_START + i;
15092
15093     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15094   }
15095
15096   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15097   {
15098     int element = EL_GROUP_START + i;
15099
15100     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15101   }
15102
15103   for (i = 0; i < 4; i++)
15104   {
15105     for (j = 0; j < NUM_BELT_PARTS; j++)
15106     {
15107       int element = belt_base_active_element[i] + j;
15108       int graphic = el2img(element);
15109       int anim_mode = graphic_info[graphic].anim_mode;
15110
15111       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15112       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15113     }
15114   }
15115 }
15116
15117 static void LoadEngineSnapshotValues_RND(void)
15118 {
15119   unsigned int num_random_calls = game.num_random_calls;
15120   int i, j;
15121
15122   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15123   {
15124     int element = EL_CUSTOM_START + i;
15125
15126     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15127   }
15128
15129   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15130   {
15131     int element = EL_GROUP_START + i;
15132
15133     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15134   }
15135
15136   for (i = 0; i < 4; i++)
15137   {
15138     for (j = 0; j < NUM_BELT_PARTS; j++)
15139     {
15140       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15141       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15142
15143       graphic_info[graphic].anim_mode = anim_mode;
15144     }
15145   }
15146
15147   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15148   {
15149     InitRND(tape.random_seed);
15150     for (i = 0; i < num_random_calls; i++)
15151       RND(1);
15152   }
15153
15154   if (game.num_random_calls != num_random_calls)
15155   {
15156     Error(ERR_INFO, "number of random calls out of sync");
15157     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15158     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15159     Error(ERR_EXIT, "this should not happen -- please debug");
15160   }
15161 }
15162
15163 void FreeEngineSnapshotSingle(void)
15164 {
15165   FreeSnapshotSingle();
15166
15167   setString(&snapshot_level_identifier, NULL);
15168   snapshot_level_nr = -1;
15169 }
15170
15171 void FreeEngineSnapshotList(void)
15172 {
15173   FreeSnapshotList();
15174 }
15175
15176 static ListNode *SaveEngineSnapshotBuffers(void)
15177 {
15178   ListNode *buffers = NULL;
15179
15180   // copy some special values to a structure better suited for the snapshot
15181
15182   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15183     SaveEngineSnapshotValues_RND();
15184   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15185     SaveEngineSnapshotValues_EM();
15186   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15187     SaveEngineSnapshotValues_SP(&buffers);
15188   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15189     SaveEngineSnapshotValues_MM(&buffers);
15190
15191   // save values stored in special snapshot structure
15192
15193   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15194     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15195   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15196     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15197   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15198     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15199   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15200     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15201
15202   // save further RND engine values
15203
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15207
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15212
15213   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15216   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15218
15219   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15220   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15221   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15222
15223   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15224
15225   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15226
15227   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15228   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15229
15230   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15231   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15232   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15233   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15234   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15235   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15236   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15237   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15238   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15239   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15241   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15242   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15243   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15244   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15246   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15247   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15248
15249   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15250   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15251
15252   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15253   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15254   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15255
15256   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15257   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15258
15259   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15260   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15261   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15262   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15263   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15264
15265   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15266   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15267
15268 #if 0
15269   ListNode *node = engine_snapshot_list_rnd;
15270   int num_bytes = 0;
15271
15272   while (node != NULL)
15273   {
15274     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15275
15276     node = node->next;
15277   }
15278
15279   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15280 #endif
15281
15282   return buffers;
15283 }
15284
15285 void SaveEngineSnapshotSingle(void)
15286 {
15287   ListNode *buffers = SaveEngineSnapshotBuffers();
15288
15289   // finally save all snapshot buffers to single snapshot
15290   SaveSnapshotSingle(buffers);
15291
15292   // save level identification information
15293   setString(&snapshot_level_identifier, leveldir_current->identifier);
15294   snapshot_level_nr = level_nr;
15295 }
15296
15297 boolean CheckSaveEngineSnapshotToList(void)
15298 {
15299   boolean save_snapshot =
15300     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15301      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15302       game.snapshot.changed_action) ||
15303      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15304       game.snapshot.collected_item));
15305
15306   game.snapshot.changed_action = FALSE;
15307   game.snapshot.collected_item = FALSE;
15308   game.snapshot.save_snapshot = save_snapshot;
15309
15310   return save_snapshot;
15311 }
15312
15313 void SaveEngineSnapshotToList(void)
15314 {
15315   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15316       tape.quick_resume)
15317     return;
15318
15319   ListNode *buffers = SaveEngineSnapshotBuffers();
15320
15321   // finally save all snapshot buffers to snapshot list
15322   SaveSnapshotToList(buffers);
15323 }
15324
15325 void SaveEngineSnapshotToListInitial(void)
15326 {
15327   FreeEngineSnapshotList();
15328
15329   SaveEngineSnapshotToList();
15330 }
15331
15332 static void LoadEngineSnapshotValues(void)
15333 {
15334   // restore special values from snapshot structure
15335
15336   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15337     LoadEngineSnapshotValues_RND();
15338   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15339     LoadEngineSnapshotValues_EM();
15340   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15341     LoadEngineSnapshotValues_SP();
15342   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15343     LoadEngineSnapshotValues_MM();
15344 }
15345
15346 void LoadEngineSnapshotSingle(void)
15347 {
15348   LoadSnapshotSingle();
15349
15350   LoadEngineSnapshotValues();
15351 }
15352
15353 static void LoadEngineSnapshot_Undo(int steps)
15354 {
15355   LoadSnapshotFromList_Older(steps);
15356
15357   LoadEngineSnapshotValues();
15358 }
15359
15360 static void LoadEngineSnapshot_Redo(int steps)
15361 {
15362   LoadSnapshotFromList_Newer(steps);
15363
15364   LoadEngineSnapshotValues();
15365 }
15366
15367 boolean CheckEngineSnapshotSingle(void)
15368 {
15369   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15370           snapshot_level_nr == level_nr);
15371 }
15372
15373 boolean CheckEngineSnapshotList(void)
15374 {
15375   return CheckSnapshotList();
15376 }
15377
15378
15379 // ---------- new game button stuff -------------------------------------------
15380
15381 static struct
15382 {
15383   int graphic;
15384   struct XY *pos;
15385   int gadget_id;
15386   boolean *setup_value;
15387   boolean allowed_on_tape;
15388   char *infotext;
15389 } gamebutton_info[NUM_GAME_BUTTONS] =
15390 {
15391   {
15392     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15393     GAME_CTRL_ID_STOP,                          NULL,
15394     TRUE,                                       "stop game"
15395   },
15396   {
15397     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15398     GAME_CTRL_ID_PAUSE,                         NULL,
15399     TRUE,                                       "pause game"
15400   },
15401   {
15402     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15403     GAME_CTRL_ID_PLAY,                          NULL,
15404     TRUE,                                       "play game"
15405   },
15406   {
15407     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15408     GAME_CTRL_ID_UNDO,                          NULL,
15409     TRUE,                                       "undo step"
15410   },
15411   {
15412     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15413     GAME_CTRL_ID_REDO,                          NULL,
15414     TRUE,                                       "redo step"
15415   },
15416   {
15417     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15418     GAME_CTRL_ID_SAVE,                          NULL,
15419     TRUE,                                       "save game"
15420   },
15421   {
15422     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15423     GAME_CTRL_ID_PAUSE2,                        NULL,
15424     TRUE,                                       "pause game"
15425   },
15426   {
15427     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15428     GAME_CTRL_ID_LOAD,                          NULL,
15429     TRUE,                                       "load game"
15430   },
15431   {
15432     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15433     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15434     FALSE,                                      "stop game"
15435   },
15436   {
15437     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15438     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15439     FALSE,                                      "pause game"
15440   },
15441   {
15442     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15443     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15444     FALSE,                                      "play game"
15445   },
15446   {
15447     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15448     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15449     TRUE,                                       "background music on/off"
15450   },
15451   {
15452     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15453     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15454     TRUE,                                       "sound loops on/off"
15455   },
15456   {
15457     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15458     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15459     TRUE,                                       "normal sounds on/off"
15460   },
15461   {
15462     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15463     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15464     FALSE,                                      "background music on/off"
15465   },
15466   {
15467     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15468     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15469     FALSE,                                      "sound loops on/off"
15470   },
15471   {
15472     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15473     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15474     FALSE,                                      "normal sounds on/off"
15475   }
15476 };
15477
15478 void CreateGameButtons(void)
15479 {
15480   int i;
15481
15482   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15483   {
15484     int graphic = gamebutton_info[i].graphic;
15485     struct GraphicInfo *gfx = &graphic_info[graphic];
15486     struct XY *pos = gamebutton_info[i].pos;
15487     struct GadgetInfo *gi;
15488     int button_type;
15489     boolean checked;
15490     unsigned int event_mask;
15491     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15492     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15493     int base_x = (on_tape ? VX : DX);
15494     int base_y = (on_tape ? VY : DY);
15495     int gd_x   = gfx->src_x;
15496     int gd_y   = gfx->src_y;
15497     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15498     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15499     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15500     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15501     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15502     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15503     int id = i;
15504
15505     if (gfx->bitmap == NULL)
15506     {
15507       game_gadget[id] = NULL;
15508
15509       continue;
15510     }
15511
15512     if (id == GAME_CTRL_ID_STOP ||
15513         id == GAME_CTRL_ID_PANEL_STOP ||
15514         id == GAME_CTRL_ID_PLAY ||
15515         id == GAME_CTRL_ID_PANEL_PLAY ||
15516         id == GAME_CTRL_ID_SAVE ||
15517         id == GAME_CTRL_ID_LOAD)
15518     {
15519       button_type = GD_TYPE_NORMAL_BUTTON;
15520       checked = FALSE;
15521       event_mask = GD_EVENT_RELEASED;
15522     }
15523     else if (id == GAME_CTRL_ID_UNDO ||
15524              id == GAME_CTRL_ID_REDO)
15525     {
15526       button_type = GD_TYPE_NORMAL_BUTTON;
15527       checked = FALSE;
15528       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15529     }
15530     else
15531     {
15532       button_type = GD_TYPE_CHECK_BUTTON;
15533       checked = (gamebutton_info[i].setup_value != NULL ?
15534                  *gamebutton_info[i].setup_value : FALSE);
15535       event_mask = GD_EVENT_PRESSED;
15536     }
15537
15538     gi = CreateGadget(GDI_CUSTOM_ID, id,
15539                       GDI_IMAGE_ID, graphic,
15540                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15541                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15542                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15543                       GDI_WIDTH, gfx->width,
15544                       GDI_HEIGHT, gfx->height,
15545                       GDI_TYPE, button_type,
15546                       GDI_STATE, GD_BUTTON_UNPRESSED,
15547                       GDI_CHECKED, checked,
15548                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15549                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15550                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15551                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15552                       GDI_DIRECT_DRAW, FALSE,
15553                       GDI_EVENT_MASK, event_mask,
15554                       GDI_CALLBACK_ACTION, HandleGameButtons,
15555                       GDI_END);
15556
15557     if (gi == NULL)
15558       Error(ERR_EXIT, "cannot create gadget");
15559
15560     game_gadget[id] = gi;
15561   }
15562 }
15563
15564 void FreeGameButtons(void)
15565 {
15566   int i;
15567
15568   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15569     FreeGadget(game_gadget[i]);
15570 }
15571
15572 static void UnmapGameButtonsAtSamePosition(int id)
15573 {
15574   int i;
15575
15576   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15577     if (i != id &&
15578         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15579         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15580       UnmapGadget(game_gadget[i]);
15581 }
15582
15583 static void UnmapGameButtonsAtSamePosition_All(void)
15584 {
15585   if (setup.show_snapshot_buttons)
15586   {
15587     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15588     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15589     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15590   }
15591   else
15592   {
15593     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15594     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15595     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15596
15597     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15598     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15599     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15600   }
15601 }
15602
15603 static void MapGameButtonsAtSamePosition(int id)
15604 {
15605   int i;
15606
15607   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15608     if (i != id &&
15609         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15610         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15611       MapGadget(game_gadget[i]);
15612
15613   UnmapGameButtonsAtSamePosition_All();
15614 }
15615
15616 void MapUndoRedoButtons(void)
15617 {
15618   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15619   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15620
15621   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15622   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15623
15624   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15625 }
15626
15627 void UnmapUndoRedoButtons(void)
15628 {
15629   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15630   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15631
15632   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15633   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15634
15635   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15636 }
15637
15638 static void MapGameButtonsExt(boolean on_tape)
15639 {
15640   int i;
15641
15642   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15643     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15644         i != GAME_CTRL_ID_UNDO &&
15645         i != GAME_CTRL_ID_REDO)
15646       MapGadget(game_gadget[i]);
15647
15648   UnmapGameButtonsAtSamePosition_All();
15649
15650   RedrawGameButtons();
15651 }
15652
15653 static void UnmapGameButtonsExt(boolean on_tape)
15654 {
15655   int i;
15656
15657   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15658     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15659       UnmapGadget(game_gadget[i]);
15660 }
15661
15662 static void RedrawGameButtonsExt(boolean on_tape)
15663 {
15664   int i;
15665
15666   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15667     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15668       RedrawGadget(game_gadget[i]);
15669
15670   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15671   redraw_mask &= ~REDRAW_ALL;
15672 }
15673
15674 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15675 {
15676   if (gi == NULL)
15677     return;
15678
15679   gi->checked = state;
15680 }
15681
15682 static void RedrawSoundButtonGadget(int id)
15683 {
15684   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15685              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15686              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15687              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15688              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15689              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15690              id);
15691
15692   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15693   RedrawGadget(game_gadget[id2]);
15694 }
15695
15696 void MapGameButtons(void)
15697 {
15698   MapGameButtonsExt(FALSE);
15699 }
15700
15701 void UnmapGameButtons(void)
15702 {
15703   UnmapGameButtonsExt(FALSE);
15704 }
15705
15706 void RedrawGameButtons(void)
15707 {
15708   RedrawGameButtonsExt(FALSE);
15709 }
15710
15711 void MapGameButtonsOnTape(void)
15712 {
15713   MapGameButtonsExt(TRUE);
15714 }
15715
15716 void UnmapGameButtonsOnTape(void)
15717 {
15718   UnmapGameButtonsExt(TRUE);
15719 }
15720
15721 void RedrawGameButtonsOnTape(void)
15722 {
15723   RedrawGameButtonsExt(TRUE);
15724 }
15725
15726 static void GameUndoRedoExt(void)
15727 {
15728   ClearPlayerAction();
15729
15730   tape.pausing = TRUE;
15731
15732   RedrawPlayfield();
15733   UpdateAndDisplayGameControlValues();
15734
15735   DrawCompleteVideoDisplay();
15736   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15737   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15738   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15739
15740   BackToFront();
15741 }
15742
15743 static void GameUndo(int steps)
15744 {
15745   if (!CheckEngineSnapshotList())
15746     return;
15747
15748   LoadEngineSnapshot_Undo(steps);
15749
15750   GameUndoRedoExt();
15751 }
15752
15753 static void GameRedo(int steps)
15754 {
15755   if (!CheckEngineSnapshotList())
15756     return;
15757
15758   LoadEngineSnapshot_Redo(steps);
15759
15760   GameUndoRedoExt();
15761 }
15762
15763 static void HandleGameButtonsExt(int id, int button)
15764 {
15765   static boolean game_undo_executed = FALSE;
15766   int steps = BUTTON_STEPSIZE(button);
15767   boolean handle_game_buttons =
15768     (game_status == GAME_MODE_PLAYING ||
15769      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15770
15771   if (!handle_game_buttons)
15772     return;
15773
15774   switch (id)
15775   {
15776     case GAME_CTRL_ID_STOP:
15777     case GAME_CTRL_ID_PANEL_STOP:
15778       if (game_status == GAME_MODE_MAIN)
15779         break;
15780
15781       if (tape.playing)
15782         TapeStop();
15783       else
15784         RequestQuitGame(TRUE);
15785
15786       break;
15787
15788     case GAME_CTRL_ID_PAUSE:
15789     case GAME_CTRL_ID_PAUSE2:
15790     case GAME_CTRL_ID_PANEL_PAUSE:
15791       if (network.enabled && game_status == GAME_MODE_PLAYING)
15792       {
15793         if (tape.pausing)
15794           SendToServer_ContinuePlaying();
15795         else
15796           SendToServer_PausePlaying();
15797       }
15798       else
15799         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15800
15801       game_undo_executed = FALSE;
15802
15803       break;
15804
15805     case GAME_CTRL_ID_PLAY:
15806     case GAME_CTRL_ID_PANEL_PLAY:
15807       if (game_status == GAME_MODE_MAIN)
15808       {
15809         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15810       }
15811       else if (tape.pausing)
15812       {
15813         if (network.enabled)
15814           SendToServer_ContinuePlaying();
15815         else
15816           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15817       }
15818       break;
15819
15820     case GAME_CTRL_ID_UNDO:
15821       // Important: When using "save snapshot when collecting an item" mode,
15822       // load last (current) snapshot for first "undo" after pressing "pause"
15823       // (else the last-but-one snapshot would be loaded, because the snapshot
15824       // pointer already points to the last snapshot when pressing "pause",
15825       // which is fine for "every step/move" mode, but not for "every collect")
15826       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15827           !game_undo_executed)
15828         steps--;
15829
15830       game_undo_executed = TRUE;
15831
15832       GameUndo(steps);
15833       break;
15834
15835     case GAME_CTRL_ID_REDO:
15836       GameRedo(steps);
15837       break;
15838
15839     case GAME_CTRL_ID_SAVE:
15840       TapeQuickSave();
15841       break;
15842
15843     case GAME_CTRL_ID_LOAD:
15844       TapeQuickLoad();
15845       break;
15846
15847     case SOUND_CTRL_ID_MUSIC:
15848     case SOUND_CTRL_ID_PANEL_MUSIC:
15849       if (setup.sound_music)
15850       { 
15851         setup.sound_music = FALSE;
15852
15853         FadeMusic();
15854       }
15855       else if (audio.music_available)
15856       { 
15857         setup.sound = setup.sound_music = TRUE;
15858
15859         SetAudioMode(setup.sound);
15860
15861         if (game_status == GAME_MODE_PLAYING)
15862           PlayLevelMusic();
15863       }
15864
15865       RedrawSoundButtonGadget(id);
15866
15867       break;
15868
15869     case SOUND_CTRL_ID_LOOPS:
15870     case SOUND_CTRL_ID_PANEL_LOOPS:
15871       if (setup.sound_loops)
15872         setup.sound_loops = FALSE;
15873       else if (audio.loops_available)
15874       {
15875         setup.sound = setup.sound_loops = TRUE;
15876
15877         SetAudioMode(setup.sound);
15878       }
15879
15880       RedrawSoundButtonGadget(id);
15881
15882       break;
15883
15884     case SOUND_CTRL_ID_SIMPLE:
15885     case SOUND_CTRL_ID_PANEL_SIMPLE:
15886       if (setup.sound_simple)
15887         setup.sound_simple = FALSE;
15888       else if (audio.sound_available)
15889       {
15890         setup.sound = setup.sound_simple = TRUE;
15891
15892         SetAudioMode(setup.sound);
15893       }
15894
15895       RedrawSoundButtonGadget(id);
15896
15897       break;
15898
15899     default:
15900       break;
15901   }
15902 }
15903
15904 static void HandleGameButtons(struct GadgetInfo *gi)
15905 {
15906   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15907 }
15908
15909 void HandleSoundButtonKeys(Key key)
15910 {
15911   if (key == setup.shortcut.sound_simple)
15912     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15913   else if (key == setup.shortcut.sound_loops)
15914     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15915   else if (key == setup.shortcut.sound_music)
15916     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15917 }