moved (and renamed) global variables to game structure (ExitX/ExitY)
[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 IncrementSokobanFieldsNeeded(void)
1687 {
1688   if (level.sb_fields_needed)
1689     game.sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementSokobanObjectsNeeded(void)
1693 {
1694   if (level.sb_objects_needed)
1695     game.sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementSokobanFieldsNeeded(void)
1699 {
1700   if (game.sokoban_fields_still_needed > 0)
1701     game.sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementSokobanObjectsNeeded(void)
1705 {
1706   if (game.sokoban_objects_still_needed > 0)
1707     game.sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       game.lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       game.friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (game.LevelSolved ?
2211               game.LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (game.LevelSolved ?
2220                game.LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                game.score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               game.gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      game.gems_still_needed > 0 ||
2243                      game.sokoban_fields_still_needed > 0 ||
2244                      game.sokoban_objects_still_needed > 0 ||
2245                      game.lights_still_needed > 0);
2246   int health = (game.LevelSolved ?
2247                 game.LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 game.health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     game.friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     game.sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     game.sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   FadeOut(fade_mask);
3369
3370   // needed if different viewport properties defined for playing
3371   ChangeViewportPropertiesIfNeeded();
3372
3373   ClearField();
3374
3375   DrawCompleteVideoDisplay();
3376
3377   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3378
3379   InitGameEngine();
3380   InitGameControlValues();
3381
3382   // don't play tapes over network
3383   network_playing = (network.enabled && !tape.playing);
3384
3385   for (i = 0; i < MAX_PLAYERS; i++)
3386   {
3387     struct PlayerInfo *player = &stored_player[i];
3388
3389     player->index_nr = i;
3390     player->index_bit = (1 << i);
3391     player->element_nr = EL_PLAYER_1 + i;
3392
3393     player->present = FALSE;
3394     player->active = FALSE;
3395     player->mapped = FALSE;
3396
3397     player->killed = FALSE;
3398     player->reanimated = FALSE;
3399     player->buried = FALSE;
3400
3401     player->action = 0;
3402     player->effective_action = 0;
3403     player->programmed_action = 0;
3404
3405     player->mouse_action.lx = 0;
3406     player->mouse_action.ly = 0;
3407     player->mouse_action.button = 0;
3408     player->mouse_action.button_hint = 0;
3409
3410     player->effective_mouse_action.lx = 0;
3411     player->effective_mouse_action.ly = 0;
3412     player->effective_mouse_action.button = 0;
3413     player->effective_mouse_action.button_hint = 0;
3414
3415     for (j = 0; j < MAX_NUM_KEYS; j++)
3416       player->key[j] = FALSE;
3417
3418     player->num_white_keys = 0;
3419
3420     player->dynabomb_count = 0;
3421     player->dynabomb_size = 1;
3422     player->dynabombs_left = 0;
3423     player->dynabomb_xl = FALSE;
3424
3425     player->MovDir = initial_move_dir;
3426     player->MovPos = 0;
3427     player->GfxPos = 0;
3428     player->GfxDir = initial_move_dir;
3429     player->GfxAction = ACTION_DEFAULT;
3430     player->Frame = 0;
3431     player->StepFrame = 0;
3432
3433     player->initial_element = player->element_nr;
3434     player->artwork_element =
3435       (level.use_artwork_element[i] ? level.artwork_element[i] :
3436        player->element_nr);
3437     player->use_murphy = FALSE;
3438
3439     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3440     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3441
3442     player->gravity = level.initial_player_gravity[i];
3443
3444     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3445
3446     player->actual_frame_counter = 0;
3447
3448     player->step_counter = 0;
3449
3450     player->last_move_dir = initial_move_dir;
3451
3452     player->is_active = FALSE;
3453
3454     player->is_waiting = FALSE;
3455     player->is_moving = FALSE;
3456     player->is_auto_moving = FALSE;
3457     player->is_digging = FALSE;
3458     player->is_snapping = FALSE;
3459     player->is_collecting = FALSE;
3460     player->is_pushing = FALSE;
3461     player->is_switching = FALSE;
3462     player->is_dropping = FALSE;
3463     player->is_dropping_pressed = FALSE;
3464
3465     player->is_bored = FALSE;
3466     player->is_sleeping = FALSE;
3467
3468     player->was_waiting = TRUE;
3469     player->was_moving = FALSE;
3470     player->was_snapping = FALSE;
3471     player->was_dropping = FALSE;
3472
3473     player->force_dropping = FALSE;
3474
3475     player->frame_counter_bored = -1;
3476     player->frame_counter_sleeping = -1;
3477
3478     player->anim_delay_counter = 0;
3479     player->post_delay_counter = 0;
3480
3481     player->dir_waiting = initial_move_dir;
3482     player->action_waiting = ACTION_DEFAULT;
3483     player->last_action_waiting = ACTION_DEFAULT;
3484     player->special_action_bored = ACTION_DEFAULT;
3485     player->special_action_sleeping = ACTION_DEFAULT;
3486
3487     player->switch_x = -1;
3488     player->switch_y = -1;
3489
3490     player->drop_x = -1;
3491     player->drop_y = -1;
3492
3493     player->show_envelope = 0;
3494
3495     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3496
3497     player->push_delay       = -1;      // initialized when pushing starts
3498     player->push_delay_value = game.initial_push_delay_value;
3499
3500     player->drop_delay = 0;
3501     player->drop_pressed_delay = 0;
3502
3503     player->last_jx = -1;
3504     player->last_jy = -1;
3505     player->jx = -1;
3506     player->jy = -1;
3507
3508     player->shield_normal_time_left = 0;
3509     player->shield_deadly_time_left = 0;
3510
3511     player->inventory_infinite_element = EL_UNDEFINED;
3512     player->inventory_size = 0;
3513
3514     if (level.use_initial_inventory[i])
3515     {
3516       for (j = 0; j < level.initial_inventory_size[i]; j++)
3517       {
3518         int element = level.initial_inventory_content[i][j];
3519         int collect_count = element_info[element].collect_count_initial;
3520         int k;
3521
3522         if (!IS_CUSTOM_ELEMENT(element))
3523           collect_count = 1;
3524
3525         if (collect_count == 0)
3526           player->inventory_infinite_element = element;
3527         else
3528           for (k = 0; k < collect_count; k++)
3529             if (player->inventory_size < MAX_INVENTORY_SIZE)
3530               player->inventory_element[player->inventory_size++] = element;
3531       }
3532     }
3533
3534     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3535     SnapField(player, 0, 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
3548   FrameCounter = 0;
3549   TimeFrames = 0;
3550   TimePlayed = 0;
3551   TimeLeft = level.time;
3552   TapeTime = 0;
3553
3554   ScreenMovDir = MV_NONE;
3555   ScreenMovPos = 0;
3556   ScreenGfxPos = 0;
3557
3558   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3559
3560   game.exit_x = -1;
3561   game.exit_y = -1;
3562
3563   game.all_players_gone = FALSE;
3564
3565   game.LevelSolved = FALSE;
3566   game.GameOver = FALSE;
3567
3568   game.LevelSolved_GameWon = FALSE;
3569   game.LevelSolved_GameEnd = FALSE;
3570   game.LevelSolved_SaveTape = FALSE;
3571   game.LevelSolved_SaveScore = FALSE;
3572
3573   game.LevelSolved_CountingTime = 0;
3574   game.LevelSolved_CountingScore = 0;
3575   game.LevelSolved_CountingHealth = 0;
3576
3577   game.panel.active = TRUE;
3578
3579   game.no_time_limit = (level.time == 0);
3580
3581   game.yamyam_content_nr = 0;
3582   game.robot_wheel_active = FALSE;
3583   game.magic_wall_active = FALSE;
3584   game.magic_wall_time_left = 0;
3585   game.light_time_left = 0;
3586   game.timegate_time_left = 0;
3587   game.switchgate_pos = 0;
3588   game.wind_direction = level.wind_direction_initial;
3589
3590   game.score = 0;
3591   game.score_final = 0;
3592
3593   game.health = MAX_HEALTH;
3594   game.health_final = MAX_HEALTH;
3595
3596   game.gems_still_needed = level.gems_needed;
3597   game.sokoban_fields_still_needed = 0;
3598   game.sokoban_objects_still_needed = 0;
3599   game.lights_still_needed = 0;
3600   game.players_still_needed = 0;
3601   game.friends_still_needed = 0;
3602
3603   game.lenses_time_left = 0;
3604   game.magnify_time_left = 0;
3605
3606   game.ball_state = level.ball_state_initial;
3607   game.ball_content_nr = 0;
3608
3609   game.explosions_delayed = TRUE;
3610
3611   game.envelope_active = FALSE;
3612
3613   for (i = 0; i < NUM_BELTS; i++)
3614   {
3615     game.belt_dir[i] = MV_NONE;
3616     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3617   }
3618
3619   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3620     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3621
3622 #if DEBUG_INIT_PLAYER
3623   DebugPrintPlayerStatus("Player status at level initialization");
3624 #endif
3625
3626   SCAN_PLAYFIELD(x, y)
3627   {
3628     Feld[x][y] = Last[x][y] = level.field[x][y];
3629     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3630     ChangeDelay[x][y] = 0;
3631     ChangePage[x][y] = -1;
3632     CustomValue[x][y] = 0;              // initialized in InitField()
3633     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3634     AmoebaNr[x][y] = 0;
3635     WasJustMoving[x][y] = 0;
3636     WasJustFalling[x][y] = 0;
3637     CheckCollision[x][y] = 0;
3638     CheckImpact[x][y] = 0;
3639     Stop[x][y] = FALSE;
3640     Pushed[x][y] = FALSE;
3641
3642     ChangeCount[x][y] = 0;
3643     ChangeEvent[x][y] = -1;
3644
3645     ExplodePhase[x][y] = 0;
3646     ExplodeDelay[x][y] = 0;
3647     ExplodeField[x][y] = EX_TYPE_NONE;
3648
3649     RunnerVisit[x][y] = 0;
3650     PlayerVisit[x][y] = 0;
3651
3652     GfxFrame[x][y] = 0;
3653     GfxRandom[x][y] = INIT_GFX_RANDOM();
3654     GfxElement[x][y] = EL_UNDEFINED;
3655     GfxAction[x][y] = ACTION_DEFAULT;
3656     GfxDir[x][y] = MV_NONE;
3657     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3658   }
3659
3660   SCAN_PLAYFIELD(x, y)
3661   {
3662     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3663       emulate_bd = FALSE;
3664     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3665       emulate_sb = FALSE;
3666     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3667       emulate_sp = FALSE;
3668
3669     InitField(x, y, TRUE);
3670
3671     ResetGfxAnimation(x, y);
3672   }
3673
3674   InitBeltMovement();
3675
3676   for (i = 0; i < MAX_PLAYERS; i++)
3677   {
3678     struct PlayerInfo *player = &stored_player[i];
3679
3680     // set number of special actions for bored and sleeping animation
3681     player->num_special_action_bored =
3682       get_num_special_action(player->artwork_element,
3683                              ACTION_BORING_1, ACTION_BORING_LAST);
3684     player->num_special_action_sleeping =
3685       get_num_special_action(player->artwork_element,
3686                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3687   }
3688
3689   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3690                     emulate_sb ? EMU_SOKOBAN :
3691                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3692
3693   // initialize type of slippery elements
3694   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3695   {
3696     if (!IS_CUSTOM_ELEMENT(i))
3697     {
3698       // default: elements slip down either to the left or right randomly
3699       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3700
3701       // SP style elements prefer to slip down on the left side
3702       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3703         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3704
3705       // BD style elements prefer to slip down on the left side
3706       if (game.emulation == EMU_BOULDERDASH)
3707         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3708     }
3709   }
3710
3711   // initialize explosion and ignition delay
3712   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3713   {
3714     if (!IS_CUSTOM_ELEMENT(i))
3715     {
3716       int num_phase = 8;
3717       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3718                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3719                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3720       int last_phase = (num_phase + 1) * delay;
3721       int half_phase = (num_phase / 2) * delay;
3722
3723       element_info[i].explosion_delay = last_phase - 1;
3724       element_info[i].ignition_delay = half_phase;
3725
3726       if (i == EL_BLACK_ORB)
3727         element_info[i].ignition_delay = 1;
3728     }
3729   }
3730
3731   // correct non-moving belts to start moving left
3732   for (i = 0; i < NUM_BELTS; i++)
3733     if (game.belt_dir[i] == MV_NONE)
3734       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3735
3736 #if USE_NEW_PLAYER_ASSIGNMENTS
3737   for (i = 0; i < MAX_PLAYERS; i++)
3738   {
3739     stored_player[i].connected = FALSE;
3740
3741     // in network game mode, the local player might not be the first player
3742     if (stored_player[i].connected_locally)
3743       local_player = &stored_player[i];
3744   }
3745
3746   if (!network.enabled)
3747     local_player->connected = TRUE;
3748
3749   if (tape.playing)
3750   {
3751     for (i = 0; i < MAX_PLAYERS; i++)
3752       stored_player[i].connected = tape.player_participates[i];
3753   }
3754   else if (network.enabled)
3755   {
3756     // add team mode players connected over the network (needed for correct
3757     // assignment of player figures from level to locally playing players)
3758
3759     for (i = 0; i < MAX_PLAYERS; i++)
3760       if (stored_player[i].connected_network)
3761         stored_player[i].connected = TRUE;
3762   }
3763   else if (game.team_mode)
3764   {
3765     // try to guess locally connected team mode players (needed for correct
3766     // assignment of player figures from level to locally playing players)
3767
3768     for (i = 0; i < MAX_PLAYERS; i++)
3769       if (setup.input[i].use_joystick ||
3770           setup.input[i].key.left != KSYM_UNDEFINED)
3771         stored_player[i].connected = TRUE;
3772   }
3773
3774 #if DEBUG_INIT_PLAYER
3775   DebugPrintPlayerStatus("Player status after level initialization");
3776 #endif
3777
3778 #if DEBUG_INIT_PLAYER
3779   if (options.debug)
3780     printf("Reassigning players ...\n");
3781 #endif
3782
3783   // check if any connected player was not found in playfield
3784   for (i = 0; i < MAX_PLAYERS; i++)
3785   {
3786     struct PlayerInfo *player = &stored_player[i];
3787
3788     if (player->connected && !player->present)
3789     {
3790       struct PlayerInfo *field_player = NULL;
3791
3792 #if DEBUG_INIT_PLAYER
3793       if (options.debug)
3794         printf("- looking for field player for player %d ...\n", i + 1);
3795 #endif
3796
3797       // assign first free player found that is present in the playfield
3798
3799       // first try: look for unmapped playfield player that is not connected
3800       for (j = 0; j < MAX_PLAYERS; j++)
3801         if (field_player == NULL &&
3802             stored_player[j].present &&
3803             !stored_player[j].mapped &&
3804             !stored_player[j].connected)
3805           field_player = &stored_player[j];
3806
3807       // second try: look for *any* unmapped playfield player
3808       for (j = 0; j < MAX_PLAYERS; j++)
3809         if (field_player == NULL &&
3810             stored_player[j].present &&
3811             !stored_player[j].mapped)
3812           field_player = &stored_player[j];
3813
3814       if (field_player != NULL)
3815       {
3816         int jx = field_player->jx, jy = field_player->jy;
3817
3818 #if DEBUG_INIT_PLAYER
3819         if (options.debug)
3820           printf("- found player %d\n", field_player->index_nr + 1);
3821 #endif
3822
3823         player->present = FALSE;
3824         player->active = FALSE;
3825
3826         field_player->present = TRUE;
3827         field_player->active = TRUE;
3828
3829         /*
3830         player->initial_element = field_player->initial_element;
3831         player->artwork_element = field_player->artwork_element;
3832
3833         player->block_last_field       = field_player->block_last_field;
3834         player->block_delay_adjustment = field_player->block_delay_adjustment;
3835         */
3836
3837         StorePlayer[jx][jy] = field_player->element_nr;
3838
3839         field_player->jx = field_player->last_jx = jx;
3840         field_player->jy = field_player->last_jy = jy;
3841
3842         if (local_player == player)
3843           local_player = field_player;
3844
3845         map_player_action[field_player->index_nr] = i;
3846
3847         field_player->mapped = TRUE;
3848
3849 #if DEBUG_INIT_PLAYER
3850         if (options.debug)
3851           printf("- map_player_action[%d] == %d\n",
3852                  field_player->index_nr + 1, i + 1);
3853 #endif
3854       }
3855     }
3856
3857     if (player->connected && player->present)
3858       player->mapped = TRUE;
3859   }
3860
3861 #if DEBUG_INIT_PLAYER
3862   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3863 #endif
3864
3865 #else
3866
3867   // check if any connected player was not found in playfield
3868   for (i = 0; i < MAX_PLAYERS; i++)
3869   {
3870     struct PlayerInfo *player = &stored_player[i];
3871
3872     if (player->connected && !player->present)
3873     {
3874       for (j = 0; j < MAX_PLAYERS; j++)
3875       {
3876         struct PlayerInfo *field_player = &stored_player[j];
3877         int jx = field_player->jx, jy = field_player->jy;
3878
3879         // assign first free player found that is present in the playfield
3880         if (field_player->present && !field_player->connected)
3881         {
3882           player->present = TRUE;
3883           player->active = TRUE;
3884
3885           field_player->present = FALSE;
3886           field_player->active = FALSE;
3887
3888           player->initial_element = field_player->initial_element;
3889           player->artwork_element = field_player->artwork_element;
3890
3891           player->block_last_field       = field_player->block_last_field;
3892           player->block_delay_adjustment = field_player->block_delay_adjustment;
3893
3894           StorePlayer[jx][jy] = player->element_nr;
3895
3896           player->jx = player->last_jx = jx;
3897           player->jy = player->last_jy = jy;
3898
3899           break;
3900         }
3901       }
3902     }
3903   }
3904 #endif
3905
3906 #if 0
3907   printf("::: local_player->present == %d\n", local_player->present);
3908 #endif
3909
3910   // set focus to local player for network games, else to all players
3911   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3912   game.centered_player_nr_next = game.centered_player_nr;
3913   game.set_centered_player = FALSE;
3914
3915   if (network_playing && tape.recording)
3916   {
3917     // store client dependent player focus when recording network games
3918     tape.centered_player_nr_next = game.centered_player_nr_next;
3919     tape.set_centered_player = TRUE;
3920   }
3921
3922   if (tape.playing)
3923   {
3924     // when playing a tape, eliminate all players who do not participate
3925
3926 #if USE_NEW_PLAYER_ASSIGNMENTS
3927
3928     if (!game.team_mode)
3929     {
3930       for (i = 0; i < MAX_PLAYERS; i++)
3931       {
3932         if (stored_player[i].active &&
3933             !tape.player_participates[map_player_action[i]])
3934         {
3935           struct PlayerInfo *player = &stored_player[i];
3936           int jx = player->jx, jy = player->jy;
3937
3938 #if DEBUG_INIT_PLAYER
3939           if (options.debug)
3940             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3941 #endif
3942
3943           player->active = FALSE;
3944           StorePlayer[jx][jy] = 0;
3945           Feld[jx][jy] = EL_EMPTY;
3946         }
3947       }
3948     }
3949
3950 #else
3951
3952     for (i = 0; i < MAX_PLAYERS; i++)
3953     {
3954       if (stored_player[i].active &&
3955           !tape.player_participates[i])
3956       {
3957         struct PlayerInfo *player = &stored_player[i];
3958         int jx = player->jx, jy = player->jy;
3959
3960         player->active = FALSE;
3961         StorePlayer[jx][jy] = 0;
3962         Feld[jx][jy] = EL_EMPTY;
3963       }
3964     }
3965 #endif
3966   }
3967   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3968   {
3969     // when in single player mode, eliminate all but the local player
3970
3971     for (i = 0; i < MAX_PLAYERS; i++)
3972     {
3973       struct PlayerInfo *player = &stored_player[i];
3974
3975       if (player->active && player != local_player)
3976       {
3977         int jx = player->jx, jy = player->jy;
3978
3979         player->active = FALSE;
3980         player->present = FALSE;
3981
3982         StorePlayer[jx][jy] = 0;
3983         Feld[jx][jy] = EL_EMPTY;
3984       }
3985     }
3986   }
3987
3988   for (i = 0; i < MAX_PLAYERS; i++)
3989     if (stored_player[i].active)
3990       game.players_still_needed++;
3991
3992   if (level.solved_by_one_player)
3993     game.players_still_needed = 1;
3994
3995   // when recording the game, store which players take part in the game
3996   if (tape.recording)
3997   {
3998 #if USE_NEW_PLAYER_ASSIGNMENTS
3999     for (i = 0; i < MAX_PLAYERS; i++)
4000       if (stored_player[i].connected)
4001         tape.player_participates[i] = TRUE;
4002 #else
4003     for (i = 0; i < MAX_PLAYERS; i++)
4004       if (stored_player[i].active)
4005         tape.player_participates[i] = TRUE;
4006 #endif
4007   }
4008
4009 #if DEBUG_INIT_PLAYER
4010   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4011 #endif
4012
4013   if (BorderElement == EL_EMPTY)
4014   {
4015     SBX_Left = 0;
4016     SBX_Right = lev_fieldx - SCR_FIELDX;
4017     SBY_Upper = 0;
4018     SBY_Lower = lev_fieldy - SCR_FIELDY;
4019   }
4020   else
4021   {
4022     SBX_Left = -1;
4023     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4024     SBY_Upper = -1;
4025     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4026   }
4027
4028   if (full_lev_fieldx <= SCR_FIELDX)
4029     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4030   if (full_lev_fieldy <= SCR_FIELDY)
4031     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4032
4033   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4034     SBX_Left--;
4035   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4036     SBY_Upper--;
4037
4038   // if local player not found, look for custom element that might create
4039   // the player (make some assumptions about the right custom element)
4040   if (!local_player->present)
4041   {
4042     int start_x = 0, start_y = 0;
4043     int found_rating = 0;
4044     int found_element = EL_UNDEFINED;
4045     int player_nr = local_player->index_nr;
4046
4047     SCAN_PLAYFIELD(x, y)
4048     {
4049       int element = Feld[x][y];
4050       int content;
4051       int xx, yy;
4052       boolean is_player;
4053
4054       if (level.use_start_element[player_nr] &&
4055           level.start_element[player_nr] == element &&
4056           found_rating < 4)
4057       {
4058         start_x = x;
4059         start_y = y;
4060
4061         found_rating = 4;
4062         found_element = element;
4063       }
4064
4065       if (!IS_CUSTOM_ELEMENT(element))
4066         continue;
4067
4068       if (CAN_CHANGE(element))
4069       {
4070         for (i = 0; i < element_info[element].num_change_pages; i++)
4071         {
4072           // check for player created from custom element as single target
4073           content = element_info[element].change_page[i].target_element;
4074           is_player = ELEM_IS_PLAYER(content);
4075
4076           if (is_player && (found_rating < 3 ||
4077                             (found_rating == 3 && element < found_element)))
4078           {
4079             start_x = x;
4080             start_y = y;
4081
4082             found_rating = 3;
4083             found_element = element;
4084           }
4085         }
4086       }
4087
4088       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4089       {
4090         // check for player created from custom element as explosion content
4091         content = element_info[element].content.e[xx][yy];
4092         is_player = ELEM_IS_PLAYER(content);
4093
4094         if (is_player && (found_rating < 2 ||
4095                           (found_rating == 2 && element < found_element)))
4096         {
4097           start_x = x + xx - 1;
4098           start_y = y + yy - 1;
4099
4100           found_rating = 2;
4101           found_element = element;
4102         }
4103
4104         if (!CAN_CHANGE(element))
4105           continue;
4106
4107         for (i = 0; i < element_info[element].num_change_pages; i++)
4108         {
4109           // check for player created from custom element as extended target
4110           content =
4111             element_info[element].change_page[i].target_content.e[xx][yy];
4112
4113           is_player = ELEM_IS_PLAYER(content);
4114
4115           if (is_player && (found_rating < 1 ||
4116                             (found_rating == 1 && element < found_element)))
4117           {
4118             start_x = x + xx - 1;
4119             start_y = y + yy - 1;
4120
4121             found_rating = 1;
4122             found_element = element;
4123           }
4124         }
4125       }
4126     }
4127
4128     scroll_x = SCROLL_POSITION_X(start_x);
4129     scroll_y = SCROLL_POSITION_Y(start_y);
4130   }
4131   else
4132   {
4133     scroll_x = SCROLL_POSITION_X(local_player->jx);
4134     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4135   }
4136
4137   // !!! FIX THIS (START) !!!
4138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4139   {
4140     InitGameEngine_EM();
4141   }
4142   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4143   {
4144     InitGameEngine_SP();
4145   }
4146   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4147   {
4148     InitGameEngine_MM();
4149   }
4150   else
4151   {
4152     DrawLevel(REDRAW_FIELD);
4153     DrawAllPlayers();
4154
4155     // after drawing the level, correct some elements
4156     if (game.timegate_time_left == 0)
4157       CloseAllOpenTimegates();
4158   }
4159
4160   // blit playfield from scroll buffer to normal back buffer for fading in
4161   BlitScreenToBitmap(backbuffer);
4162   // !!! FIX THIS (END) !!!
4163
4164   DrawMaskedBorder(fade_mask);
4165
4166   FadeIn(fade_mask);
4167
4168 #if 1
4169   // full screen redraw is required at this point in the following cases:
4170   // - special editor door undrawn when game was started from level editor
4171   // - drawing area (playfield) was changed and has to be removed completely
4172   redraw_mask = REDRAW_ALL;
4173   BackToFront();
4174 #endif
4175
4176   if (!game.restart_level)
4177   {
4178     // copy default game door content to main double buffer
4179
4180     // !!! CHECK AGAIN !!!
4181     SetPanelBackground();
4182     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4183     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4184   }
4185
4186   SetPanelBackground();
4187   SetDrawBackgroundMask(REDRAW_DOOR_1);
4188
4189   UpdateAndDisplayGameControlValues();
4190
4191   if (!game.restart_level)
4192   {
4193     UnmapGameButtons();
4194     UnmapTapeButtons();
4195
4196     FreeGameButtons();
4197     CreateGameButtons();
4198
4199     MapGameButtons();
4200     MapTapeButtons();
4201
4202     // copy actual game door content to door double buffer for OpenDoor()
4203     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4204
4205     OpenDoor(DOOR_OPEN_ALL);
4206
4207     KeyboardAutoRepeatOffUnlessAutoplay();
4208
4209 #if DEBUG_INIT_PLAYER
4210     DebugPrintPlayerStatus("Player status (final)");
4211 #endif
4212   }
4213
4214   UnmapAllGadgets();
4215
4216   MapGameButtons();
4217   MapTapeButtons();
4218
4219   if (!game.restart_level && !tape.playing)
4220   {
4221     LevelStats_incPlayed(level_nr);
4222
4223     SaveLevelSetup_SeriesInfo();
4224   }
4225
4226   game.restart_level = FALSE;
4227   game.restart_game_message = NULL;
4228   game.request_active = FALSE;
4229
4230   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4231     InitGameActions_MM();
4232
4233   SaveEngineSnapshotToListInitial();
4234
4235   if (!game.restart_level)
4236   {
4237     PlaySound(SND_GAME_STARTING);
4238
4239     if (setup.sound_music)
4240       PlayLevelMusic();
4241   }
4242 }
4243
4244 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4245                         int actual_player_x, int actual_player_y)
4246 {
4247   // this is used for non-R'n'D game engines to update certain engine values
4248
4249   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4250   {
4251     actual_player_x = correctLevelPosX_EM(actual_player_x);
4252     actual_player_y = correctLevelPosY_EM(actual_player_y);
4253   }
4254
4255   // needed to determine if sounds are played within the visible screen area
4256   scroll_x = actual_scroll_x;
4257   scroll_y = actual_scroll_y;
4258
4259   // needed to get player position for "follow finger" playing input method
4260   local_player->jx = actual_player_x;
4261   local_player->jy = actual_player_y;
4262 }
4263
4264 void InitMovDir(int x, int y)
4265 {
4266   int i, element = Feld[x][y];
4267   static int xy[4][2] =
4268   {
4269     {  0, +1 },
4270     { +1,  0 },
4271     {  0, -1 },
4272     { -1,  0 }
4273   };
4274   static int direction[3][4] =
4275   {
4276     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4277     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4278     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4279   };
4280
4281   switch (element)
4282   {
4283     case EL_BUG_RIGHT:
4284     case EL_BUG_UP:
4285     case EL_BUG_LEFT:
4286     case EL_BUG_DOWN:
4287       Feld[x][y] = EL_BUG;
4288       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4289       break;
4290
4291     case EL_SPACESHIP_RIGHT:
4292     case EL_SPACESHIP_UP:
4293     case EL_SPACESHIP_LEFT:
4294     case EL_SPACESHIP_DOWN:
4295       Feld[x][y] = EL_SPACESHIP;
4296       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4297       break;
4298
4299     case EL_BD_BUTTERFLY_RIGHT:
4300     case EL_BD_BUTTERFLY_UP:
4301     case EL_BD_BUTTERFLY_LEFT:
4302     case EL_BD_BUTTERFLY_DOWN:
4303       Feld[x][y] = EL_BD_BUTTERFLY;
4304       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4305       break;
4306
4307     case EL_BD_FIREFLY_RIGHT:
4308     case EL_BD_FIREFLY_UP:
4309     case EL_BD_FIREFLY_LEFT:
4310     case EL_BD_FIREFLY_DOWN:
4311       Feld[x][y] = EL_BD_FIREFLY;
4312       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4313       break;
4314
4315     case EL_PACMAN_RIGHT:
4316     case EL_PACMAN_UP:
4317     case EL_PACMAN_LEFT:
4318     case EL_PACMAN_DOWN:
4319       Feld[x][y] = EL_PACMAN;
4320       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4321       break;
4322
4323     case EL_YAMYAM_LEFT:
4324     case EL_YAMYAM_RIGHT:
4325     case EL_YAMYAM_UP:
4326     case EL_YAMYAM_DOWN:
4327       Feld[x][y] = EL_YAMYAM;
4328       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4329       break;
4330
4331     case EL_SP_SNIKSNAK:
4332       MovDir[x][y] = MV_UP;
4333       break;
4334
4335     case EL_SP_ELECTRON:
4336       MovDir[x][y] = MV_LEFT;
4337       break;
4338
4339     case EL_MOLE_LEFT:
4340     case EL_MOLE_RIGHT:
4341     case EL_MOLE_UP:
4342     case EL_MOLE_DOWN:
4343       Feld[x][y] = EL_MOLE;
4344       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4345       break;
4346
4347     default:
4348       if (IS_CUSTOM_ELEMENT(element))
4349       {
4350         struct ElementInfo *ei = &element_info[element];
4351         int move_direction_initial = ei->move_direction_initial;
4352         int move_pattern = ei->move_pattern;
4353
4354         if (move_direction_initial == MV_START_PREVIOUS)
4355         {
4356           if (MovDir[x][y] != MV_NONE)
4357             return;
4358
4359           move_direction_initial = MV_START_AUTOMATIC;
4360         }
4361
4362         if (move_direction_initial == MV_START_RANDOM)
4363           MovDir[x][y] = 1 << RND(4);
4364         else if (move_direction_initial & MV_ANY_DIRECTION)
4365           MovDir[x][y] = move_direction_initial;
4366         else if (move_pattern == MV_ALL_DIRECTIONS ||
4367                  move_pattern == MV_TURNING_LEFT ||
4368                  move_pattern == MV_TURNING_RIGHT ||
4369                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4370                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4371                  move_pattern == MV_TURNING_RANDOM)
4372           MovDir[x][y] = 1 << RND(4);
4373         else if (move_pattern == MV_HORIZONTAL)
4374           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4375         else if (move_pattern == MV_VERTICAL)
4376           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4377         else if (move_pattern & MV_ANY_DIRECTION)
4378           MovDir[x][y] = element_info[element].move_pattern;
4379         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4380                  move_pattern == MV_ALONG_RIGHT_SIDE)
4381         {
4382           // use random direction as default start direction
4383           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4384             MovDir[x][y] = 1 << RND(4);
4385
4386           for (i = 0; i < NUM_DIRECTIONS; i++)
4387           {
4388             int x1 = x + xy[i][0];
4389             int y1 = y + xy[i][1];
4390
4391             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4392             {
4393               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4394                 MovDir[x][y] = direction[0][i];
4395               else
4396                 MovDir[x][y] = direction[1][i];
4397
4398               break;
4399             }
4400           }
4401         }                
4402       }
4403       else
4404       {
4405         MovDir[x][y] = 1 << RND(4);
4406
4407         if (element != EL_BUG &&
4408             element != EL_SPACESHIP &&
4409             element != EL_BD_BUTTERFLY &&
4410             element != EL_BD_FIREFLY)
4411           break;
4412
4413         for (i = 0; i < NUM_DIRECTIONS; i++)
4414         {
4415           int x1 = x + xy[i][0];
4416           int y1 = y + xy[i][1];
4417
4418           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4419           {
4420             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4421             {
4422               MovDir[x][y] = direction[0][i];
4423               break;
4424             }
4425             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4426                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4427             {
4428               MovDir[x][y] = direction[1][i];
4429               break;
4430             }
4431           }
4432         }
4433       }
4434       break;
4435   }
4436
4437   GfxDir[x][y] = MovDir[x][y];
4438 }
4439
4440 void InitAmoebaNr(int x, int y)
4441 {
4442   int i;
4443   int group_nr = AmoebeNachbarNr(x, y);
4444
4445   if (group_nr == 0)
4446   {
4447     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4448     {
4449       if (AmoebaCnt[i] == 0)
4450       {
4451         group_nr = i;
4452         break;
4453       }
4454     }
4455   }
4456
4457   AmoebaNr[x][y] = group_nr;
4458   AmoebaCnt[group_nr]++;
4459   AmoebaCnt2[group_nr]++;
4460 }
4461
4462 static void LevelSolved(void)
4463 {
4464   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4465       game.players_still_needed > 0)
4466     return;
4467
4468   game.LevelSolved = TRUE;
4469   game.GameOver = TRUE;
4470
4471   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4472                       level.native_em_level->lev->score :
4473                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4474                       game_mm.score :
4475                       game.score);
4476   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4477                        MM_HEALTH(game_mm.laser_overload_value) :
4478                        game.health);
4479
4480   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4481   game.LevelSolved_CountingScore = game.score_final;
4482   game.LevelSolved_CountingHealth = game.health_final;
4483 }
4484
4485 void GameWon(void)
4486 {
4487   static int time_count_steps;
4488   static int time, time_final;
4489   static int score, score_final;
4490   static int health, health_final;
4491   static int game_over_delay_1 = 0;
4492   static int game_over_delay_2 = 0;
4493   static int game_over_delay_3 = 0;
4494   int game_over_delay_value_1 = 50;
4495   int game_over_delay_value_2 = 25;
4496   int game_over_delay_value_3 = 50;
4497
4498   if (!game.LevelSolved_GameWon)
4499   {
4500     int i;
4501
4502     // do not start end game actions before the player stops moving (to exit)
4503     if (local_player->MovPos)
4504       return;
4505
4506     game.LevelSolved_GameWon = TRUE;
4507     game.LevelSolved_SaveTape = tape.recording;
4508     game.LevelSolved_SaveScore = !tape.playing;
4509
4510     if (!tape.playing)
4511     {
4512       LevelStats_incSolved(level_nr);
4513
4514       SaveLevelSetup_SeriesInfo();
4515     }
4516
4517     if (tape.auto_play)         // tape might already be stopped here
4518       tape.auto_play_level_solved = TRUE;
4519
4520     TapeStop();
4521
4522     game_over_delay_1 = 0;
4523     game_over_delay_2 = 0;
4524     game_over_delay_3 = game_over_delay_value_3;
4525
4526     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4527     score = score_final = game.score_final;
4528     health = health_final = game.health_final;
4529
4530     if (level.score[SC_TIME_BONUS] > 0)
4531     {
4532       if (TimeLeft > 0)
4533       {
4534         time_final = 0;
4535         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4536       }
4537       else if (game.no_time_limit && TimePlayed < 999)
4538       {
4539         time_final = 999;
4540         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4541       }
4542
4543       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4544
4545       game_over_delay_1 = game_over_delay_value_1;
4546
4547       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4548       {
4549         health_final = 0;
4550         score_final += health * level.score[SC_TIME_BONUS];
4551
4552         game_over_delay_2 = game_over_delay_value_2;
4553       }
4554
4555       game.score_final = score_final;
4556       game.health_final = health_final;
4557     }
4558
4559     if (level_editor_test_game)
4560     {
4561       time = time_final;
4562       score = score_final;
4563
4564       game.LevelSolved_CountingTime = time;
4565       game.LevelSolved_CountingScore = score;
4566
4567       game_panel_controls[GAME_PANEL_TIME].value = time;
4568       game_panel_controls[GAME_PANEL_SCORE].value = score;
4569
4570       DisplayGameControlValues();
4571     }
4572
4573     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4574     {
4575       // check if last player has left the level
4576       if (game.exit_x >= 0 &&
4577           game.exit_y >= 0)
4578       {
4579         int x = game.exit_x;
4580         int y = game.exit_y;
4581         int element = Feld[x][y];
4582
4583         // close exit door after last player
4584         if ((game.all_players_gone &&
4585              (element == EL_EXIT_OPEN ||
4586               element == EL_SP_EXIT_OPEN ||
4587               element == EL_STEEL_EXIT_OPEN)) ||
4588             element == EL_EM_EXIT_OPEN ||
4589             element == EL_EM_STEEL_EXIT_OPEN)
4590         {
4591
4592           Feld[x][y] =
4593             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4594              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4595              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4596              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4597              EL_EM_STEEL_EXIT_CLOSING);
4598
4599           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4600         }
4601
4602         // player disappears
4603         DrawLevelField(x, y);
4604       }
4605
4606       for (i = 0; i < MAX_PLAYERS; i++)
4607       {
4608         struct PlayerInfo *player = &stored_player[i];
4609
4610         if (player->present)
4611         {
4612           RemovePlayer(player);
4613
4614           // player disappears
4615           DrawLevelField(player->jx, player->jy);
4616         }
4617       }
4618     }
4619
4620     PlaySound(SND_GAME_WINNING);
4621   }
4622
4623   if (game_over_delay_1 > 0)
4624   {
4625     game_over_delay_1--;
4626
4627     return;
4628   }
4629
4630   if (time != time_final)
4631   {
4632     int time_to_go = ABS(time_final - time);
4633     int time_count_dir = (time < time_final ? +1 : -1);
4634
4635     if (time_to_go < time_count_steps)
4636       time_count_steps = 1;
4637
4638     time  += time_count_steps * time_count_dir;
4639     score += time_count_steps * level.score[SC_TIME_BONUS];
4640
4641     game.LevelSolved_CountingTime = time;
4642     game.LevelSolved_CountingScore = score;
4643
4644     game_panel_controls[GAME_PANEL_TIME].value = time;
4645     game_panel_controls[GAME_PANEL_SCORE].value = score;
4646
4647     DisplayGameControlValues();
4648
4649     if (time == time_final)
4650       StopSound(SND_GAME_LEVELTIME_BONUS);
4651     else if (setup.sound_loops)
4652       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4653     else
4654       PlaySound(SND_GAME_LEVELTIME_BONUS);
4655
4656     return;
4657   }
4658
4659   if (game_over_delay_2 > 0)
4660   {
4661     game_over_delay_2--;
4662
4663     return;
4664   }
4665
4666   if (health != health_final)
4667   {
4668     int health_count_dir = (health < health_final ? +1 : -1);
4669
4670     health += health_count_dir;
4671     score  += level.score[SC_TIME_BONUS];
4672
4673     game.LevelSolved_CountingHealth = health;
4674     game.LevelSolved_CountingScore = score;
4675
4676     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4677     game_panel_controls[GAME_PANEL_SCORE].value = score;
4678
4679     DisplayGameControlValues();
4680
4681     if (health == health_final)
4682       StopSound(SND_GAME_LEVELTIME_BONUS);
4683     else if (setup.sound_loops)
4684       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4685     else
4686       PlaySound(SND_GAME_LEVELTIME_BONUS);
4687
4688     return;
4689   }
4690
4691   game.panel.active = FALSE;
4692
4693   if (game_over_delay_3 > 0)
4694   {
4695     game_over_delay_3--;
4696
4697     return;
4698   }
4699
4700   GameEnd();
4701 }
4702
4703 void GameEnd(void)
4704 {
4705   // used instead of "level_nr" (needed for network games)
4706   int last_level_nr = levelset.level_nr;
4707   int hi_pos;
4708
4709   game.LevelSolved_GameEnd = TRUE;
4710
4711   if (game.LevelSolved_SaveTape)
4712   {
4713     // make sure that request dialog to save tape does not open door again
4714     if (!global.use_envelope_request)
4715       CloseDoor(DOOR_CLOSE_1);
4716
4717     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4718   }
4719
4720   // if no tape is to be saved, close both doors simultaneously
4721   CloseDoor(DOOR_CLOSE_ALL);
4722
4723   if (level_editor_test_game)
4724   {
4725     SetGameStatus(GAME_MODE_MAIN);
4726
4727     DrawMainMenu();
4728
4729     return;
4730   }
4731
4732   if (!game.LevelSolved_SaveScore)
4733   {
4734     SetGameStatus(GAME_MODE_MAIN);
4735
4736     DrawMainMenu();
4737
4738     return;
4739   }
4740
4741   if (level_nr == leveldir_current->handicap_level)
4742   {
4743     leveldir_current->handicap_level++;
4744
4745     SaveLevelSetup_SeriesInfo();
4746   }
4747
4748   if (setup.increment_levels &&
4749       level_nr < leveldir_current->last_level &&
4750       !network_playing)
4751   {
4752     level_nr++;         // advance to next level
4753     TapeErase();        // start with empty tape
4754
4755     if (setup.auto_play_next_level)
4756     {
4757       LoadLevel(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761   }
4762
4763   hi_pos = NewHiScore(last_level_nr);
4764
4765   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4766   {
4767     SetGameStatus(GAME_MODE_SCORES);
4768
4769     DrawHallOfFame(last_level_nr, hi_pos);
4770   }
4771   else if (setup.auto_play_next_level && setup.increment_levels &&
4772            last_level_nr < leveldir_current->last_level &&
4773            !network_playing)
4774   {
4775     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4776   }
4777   else
4778   {
4779     SetGameStatus(GAME_MODE_MAIN);
4780
4781     DrawMainMenu();
4782   }
4783 }
4784
4785 int NewHiScore(int level_nr)
4786 {
4787   int k, l;
4788   int position = -1;
4789   boolean one_score_entry_per_name = !program.many_scores_per_name;
4790
4791   LoadScore(level_nr);
4792
4793   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4794       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4795     return -1;
4796
4797   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4798   {
4799     if (game.score_final > highscore[k].Score)
4800     {
4801       // player has made it to the hall of fame
4802
4803       if (k < MAX_SCORE_ENTRIES - 1)
4804       {
4805         int m = MAX_SCORE_ENTRIES - 1;
4806
4807         if (one_score_entry_per_name)
4808         {
4809           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4810             if (strEqual(setup.player_name, highscore[l].Name))
4811               m = l;
4812
4813           if (m == k)   // player's new highscore overwrites his old one
4814             goto put_into_list;
4815         }
4816
4817         for (l = m; l > k; l--)
4818         {
4819           strcpy(highscore[l].Name, highscore[l - 1].Name);
4820           highscore[l].Score = highscore[l - 1].Score;
4821         }
4822       }
4823
4824       put_into_list:
4825
4826       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4827       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4828       highscore[k].Score = game.score_final;
4829       position = k;
4830
4831       break;
4832     }
4833     else if (one_score_entry_per_name &&
4834              !strncmp(setup.player_name, highscore[k].Name,
4835                       MAX_PLAYER_NAME_LEN))
4836       break;    // player already there with a higher score
4837   }
4838
4839   if (position >= 0) 
4840     SaveScore(level_nr);
4841
4842   return position;
4843 }
4844
4845 static int getElementMoveStepsizeExt(int x, int y, int direction)
4846 {
4847   int element = Feld[x][y];
4848   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4849   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4850   int horiz_move = (dx != 0);
4851   int sign = (horiz_move ? dx : dy);
4852   int step = sign * element_info[element].move_stepsize;
4853
4854   // special values for move stepsize for spring and things on conveyor belt
4855   if (horiz_move)
4856   {
4857     if (CAN_FALL(element) &&
4858         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4859       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4860     else if (element == EL_SPRING)
4861       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4862   }
4863
4864   return step;
4865 }
4866
4867 static int getElementMoveStepsize(int x, int y)
4868 {
4869   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4870 }
4871
4872 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4873 {
4874   if (player->GfxAction != action || player->GfxDir != dir)
4875   {
4876     player->GfxAction = action;
4877     player->GfxDir = dir;
4878     player->Frame = 0;
4879     player->StepFrame = 0;
4880   }
4881 }
4882
4883 static void ResetGfxFrame(int x, int y)
4884 {
4885   // profiling showed that "autotest" spends 10~20% of its time in this function
4886   if (DrawingDeactivatedField())
4887     return;
4888
4889   int element = Feld[x][y];
4890   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4891
4892   if (graphic_info[graphic].anim_global_sync)
4893     GfxFrame[x][y] = FrameCounter;
4894   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4895     GfxFrame[x][y] = CustomValue[x][y];
4896   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4897     GfxFrame[x][y] = element_info[element].collect_score;
4898   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4899     GfxFrame[x][y] = ChangeDelay[x][y];
4900 }
4901
4902 static void ResetGfxAnimation(int x, int y)
4903 {
4904   GfxAction[x][y] = ACTION_DEFAULT;
4905   GfxDir[x][y] = MovDir[x][y];
4906   GfxFrame[x][y] = 0;
4907
4908   ResetGfxFrame(x, y);
4909 }
4910
4911 static void ResetRandomAnimationValue(int x, int y)
4912 {
4913   GfxRandom[x][y] = INIT_GFX_RANDOM();
4914 }
4915
4916 static void InitMovingField(int x, int y, int direction)
4917 {
4918   int element = Feld[x][y];
4919   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4920   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4921   int newx = x + dx;
4922   int newy = y + dy;
4923   boolean is_moving_before, is_moving_after;
4924
4925   // check if element was/is moving or being moved before/after mode change
4926   is_moving_before = (WasJustMoving[x][y] != 0);
4927   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4928
4929   // reset animation only for moving elements which change direction of moving
4930   // or which just started or stopped moving
4931   // (else CEs with property "can move" / "not moving" are reset each frame)
4932   if (is_moving_before != is_moving_after ||
4933       direction != MovDir[x][y])
4934     ResetGfxAnimation(x, y);
4935
4936   MovDir[x][y] = direction;
4937   GfxDir[x][y] = direction;
4938
4939   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4940                      direction == MV_DOWN && CAN_FALL(element) ?
4941                      ACTION_FALLING : ACTION_MOVING);
4942
4943   // this is needed for CEs with property "can move" / "not moving"
4944
4945   if (is_moving_after)
4946   {
4947     if (Feld[newx][newy] == EL_EMPTY)
4948       Feld[newx][newy] = EL_BLOCKED;
4949
4950     MovDir[newx][newy] = MovDir[x][y];
4951
4952     CustomValue[newx][newy] = CustomValue[x][y];
4953
4954     GfxFrame[newx][newy] = GfxFrame[x][y];
4955     GfxRandom[newx][newy] = GfxRandom[x][y];
4956     GfxAction[newx][newy] = GfxAction[x][y];
4957     GfxDir[newx][newy] = GfxDir[x][y];
4958   }
4959 }
4960
4961 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4962 {
4963   int direction = MovDir[x][y];
4964   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4965   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4966
4967   *goes_to_x = newx;
4968   *goes_to_y = newy;
4969 }
4970
4971 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4972 {
4973   int oldx = x, oldy = y;
4974   int direction = MovDir[x][y];
4975
4976   if (direction == MV_LEFT)
4977     oldx++;
4978   else if (direction == MV_RIGHT)
4979     oldx--;
4980   else if (direction == MV_UP)
4981     oldy++;
4982   else if (direction == MV_DOWN)
4983     oldy--;
4984
4985   *comes_from_x = oldx;
4986   *comes_from_y = oldy;
4987 }
4988
4989 static int MovingOrBlocked2Element(int x, int y)
4990 {
4991   int element = Feld[x][y];
4992
4993   if (element == EL_BLOCKED)
4994   {
4995     int oldx, oldy;
4996
4997     Blocked2Moving(x, y, &oldx, &oldy);
4998     return Feld[oldx][oldy];
4999   }
5000   else
5001     return element;
5002 }
5003
5004 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5005 {
5006   // like MovingOrBlocked2Element(), but if element is moving
5007   // and (x,y) is the field the moving element is just leaving,
5008   // return EL_BLOCKED instead of the element value
5009   int element = Feld[x][y];
5010
5011   if (IS_MOVING(x, y))
5012   {
5013     if (element == EL_BLOCKED)
5014     {
5015       int oldx, oldy;
5016
5017       Blocked2Moving(x, y, &oldx, &oldy);
5018       return Feld[oldx][oldy];
5019     }
5020     else
5021       return EL_BLOCKED;
5022   }
5023   else
5024     return element;
5025 }
5026
5027 static void RemoveField(int x, int y)
5028 {
5029   Feld[x][y] = EL_EMPTY;
5030
5031   MovPos[x][y] = 0;
5032   MovDir[x][y] = 0;
5033   MovDelay[x][y] = 0;
5034
5035   CustomValue[x][y] = 0;
5036
5037   AmoebaNr[x][y] = 0;
5038   ChangeDelay[x][y] = 0;
5039   ChangePage[x][y] = -1;
5040   Pushed[x][y] = FALSE;
5041
5042   GfxElement[x][y] = EL_UNDEFINED;
5043   GfxAction[x][y] = ACTION_DEFAULT;
5044   GfxDir[x][y] = MV_NONE;
5045 }
5046
5047 static void RemoveMovingField(int x, int y)
5048 {
5049   int oldx = x, oldy = y, newx = x, newy = y;
5050   int element = Feld[x][y];
5051   int next_element = EL_UNDEFINED;
5052
5053   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5054     return;
5055
5056   if (IS_MOVING(x, y))
5057   {
5058     Moving2Blocked(x, y, &newx, &newy);
5059
5060     if (Feld[newx][newy] != EL_BLOCKED)
5061     {
5062       // element is moving, but target field is not free (blocked), but
5063       // already occupied by something different (example: acid pool);
5064       // in this case, only remove the moving field, but not the target
5065
5066       RemoveField(oldx, oldy);
5067
5068       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5069
5070       TEST_DrawLevelField(oldx, oldy);
5071
5072       return;
5073     }
5074   }
5075   else if (element == EL_BLOCKED)
5076   {
5077     Blocked2Moving(x, y, &oldx, &oldy);
5078     if (!IS_MOVING(oldx, oldy))
5079       return;
5080   }
5081
5082   if (element == EL_BLOCKED &&
5083       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5084        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5085        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5086        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5087        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5088        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5089     next_element = get_next_element(Feld[oldx][oldy]);
5090
5091   RemoveField(oldx, oldy);
5092   RemoveField(newx, newy);
5093
5094   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5095
5096   if (next_element != EL_UNDEFINED)
5097     Feld[oldx][oldy] = next_element;
5098
5099   TEST_DrawLevelField(oldx, oldy);
5100   TEST_DrawLevelField(newx, newy);
5101 }
5102
5103 void DrawDynamite(int x, int y)
5104 {
5105   int sx = SCREENX(x), sy = SCREENY(y);
5106   int graphic = el2img(Feld[x][y]);
5107   int frame;
5108
5109   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5110     return;
5111
5112   if (IS_WALKABLE_INSIDE(Back[x][y]))
5113     return;
5114
5115   if (Back[x][y])
5116     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5117   else if (Store[x][y])
5118     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5119
5120   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5121
5122   if (Back[x][y] || Store[x][y])
5123     DrawGraphicThruMask(sx, sy, graphic, frame);
5124   else
5125     DrawGraphic(sx, sy, graphic, frame);
5126 }
5127
5128 static void CheckDynamite(int x, int y)
5129 {
5130   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5131   {
5132     MovDelay[x][y]--;
5133
5134     if (MovDelay[x][y] != 0)
5135     {
5136       DrawDynamite(x, y);
5137       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5138
5139       return;
5140     }
5141   }
5142
5143   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5144
5145   Bang(x, y);
5146 }
5147
5148 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5149 {
5150   boolean num_checked_players = 0;
5151   int i;
5152
5153   for (i = 0; i < MAX_PLAYERS; i++)
5154   {
5155     if (stored_player[i].active)
5156     {
5157       int sx = stored_player[i].jx;
5158       int sy = stored_player[i].jy;
5159
5160       if (num_checked_players == 0)
5161       {
5162         *sx1 = *sx2 = sx;
5163         *sy1 = *sy2 = sy;
5164       }
5165       else
5166       {
5167         *sx1 = MIN(*sx1, sx);
5168         *sy1 = MIN(*sy1, sy);
5169         *sx2 = MAX(*sx2, sx);
5170         *sy2 = MAX(*sy2, sy);
5171       }
5172
5173       num_checked_players++;
5174     }
5175   }
5176 }
5177
5178 static boolean checkIfAllPlayersFitToScreen_RND(void)
5179 {
5180   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5181
5182   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5183
5184   return (sx2 - sx1 < SCR_FIELDX &&
5185           sy2 - sy1 < SCR_FIELDY);
5186 }
5187
5188 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5189 {
5190   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5191
5192   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5193
5194   *sx = (sx1 + sx2) / 2;
5195   *sy = (sy1 + sy2) / 2;
5196 }
5197
5198 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5199                                boolean center_screen, boolean quick_relocation)
5200 {
5201   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5202   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5203   boolean no_delay = (tape.warp_forward);
5204   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5205   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5206   int new_scroll_x, new_scroll_y;
5207
5208   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5209   {
5210     // case 1: quick relocation inside visible screen (without scrolling)
5211
5212     RedrawPlayfield();
5213
5214     return;
5215   }
5216
5217   if (!level.shifted_relocation || center_screen)
5218   {
5219     // relocation _with_ centering of screen
5220
5221     new_scroll_x = SCROLL_POSITION_X(x);
5222     new_scroll_y = SCROLL_POSITION_Y(y);
5223   }
5224   else
5225   {
5226     // relocation _without_ centering of screen
5227
5228     int center_scroll_x = SCROLL_POSITION_X(old_x);
5229     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5230     int offset_x = x + (scroll_x - center_scroll_x);
5231     int offset_y = y + (scroll_y - center_scroll_y);
5232
5233     // for new screen position, apply previous offset to center position
5234     new_scroll_x = SCROLL_POSITION_X(offset_x);
5235     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5236   }
5237
5238   if (quick_relocation)
5239   {
5240     // case 2: quick relocation (redraw without visible scrolling)
5241
5242     scroll_x = new_scroll_x;
5243     scroll_y = new_scroll_y;
5244
5245     RedrawPlayfield();
5246
5247     return;
5248   }
5249
5250   // case 3: visible relocation (with scrolling to new position)
5251
5252   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5253
5254   SetVideoFrameDelay(wait_delay_value);
5255
5256   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5257   {
5258     int dx = 0, dy = 0;
5259     int fx = FX, fy = FY;
5260
5261     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5262     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5263
5264     if (dx == 0 && dy == 0)             // no scrolling needed at all
5265       break;
5266
5267     scroll_x -= dx;
5268     scroll_y -= dy;
5269
5270     fx += dx * TILEX / 2;
5271     fy += dy * TILEY / 2;
5272
5273     ScrollLevel(dx, dy);
5274     DrawAllPlayers();
5275
5276     // scroll in two steps of half tile size to make things smoother
5277     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5278
5279     // scroll second step to align at full tile size
5280     BlitScreenToBitmap(window);
5281   }
5282
5283   DrawAllPlayers();
5284   BackToFront();
5285
5286   SetVideoFrameDelay(frame_delay_value_old);
5287 }
5288
5289 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5290 {
5291   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5292   int player_nr = GET_PLAYER_NR(el_player);
5293   struct PlayerInfo *player = &stored_player[player_nr];
5294   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5295   boolean no_delay = (tape.warp_forward);
5296   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5297   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5298   int old_jx = player->jx;
5299   int old_jy = player->jy;
5300   int old_element = Feld[old_jx][old_jy];
5301   int element = Feld[jx][jy];
5302   boolean player_relocated = (old_jx != jx || old_jy != jy);
5303
5304   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5305   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5306   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5307   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5308   int leave_side_horiz = move_dir_horiz;
5309   int leave_side_vert  = move_dir_vert;
5310   int enter_side = enter_side_horiz | enter_side_vert;
5311   int leave_side = leave_side_horiz | leave_side_vert;
5312
5313   if (player->buried)           // do not reanimate dead player
5314     return;
5315
5316   if (!player_relocated)        // no need to relocate the player
5317     return;
5318
5319   if (IS_PLAYER(jx, jy))        // player already placed at new position
5320   {
5321     RemoveField(jx, jy);        // temporarily remove newly placed player
5322     DrawLevelField(jx, jy);
5323   }
5324
5325   if (player->present)
5326   {
5327     while (player->MovPos)
5328     {
5329       ScrollPlayer(player, SCROLL_GO_ON);
5330       ScrollScreen(NULL, SCROLL_GO_ON);
5331
5332       AdvanceFrameAndPlayerCounters(player->index_nr);
5333
5334       DrawPlayer(player);
5335
5336       BackToFront_WithFrameDelay(wait_delay_value);
5337     }
5338
5339     DrawPlayer(player);         // needed here only to cleanup last field
5340     DrawLevelField(player->jx, player->jy);     // remove player graphic
5341
5342     player->is_moving = FALSE;
5343   }
5344
5345   if (IS_CUSTOM_ELEMENT(old_element))
5346     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5347                                CE_LEFT_BY_PLAYER,
5348                                player->index_bit, leave_side);
5349
5350   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5351                                       CE_PLAYER_LEAVES_X,
5352                                       player->index_bit, leave_side);
5353
5354   Feld[jx][jy] = el_player;
5355   InitPlayerField(jx, jy, el_player, TRUE);
5356
5357   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5358      possible that the relocation target field did not contain a player element,
5359      but a walkable element, to which the new player was relocated -- in this
5360      case, restore that (already initialized!) element on the player field */
5361   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5362   {
5363     Feld[jx][jy] = element;     // restore previously existing element
5364   }
5365
5366   // only visually relocate centered player
5367   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5368                      FALSE, level.instant_relocation);
5369
5370   TestIfPlayerTouchesBadThing(jx, jy);
5371   TestIfPlayerTouchesCustomElement(jx, jy);
5372
5373   if (IS_CUSTOM_ELEMENT(element))
5374     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5375                                player->index_bit, enter_side);
5376
5377   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5378                                       player->index_bit, enter_side);
5379
5380   if (player->is_switching)
5381   {
5382     /* ensure that relocation while still switching an element does not cause
5383        a new element to be treated as also switched directly after relocation
5384        (this is important for teleporter switches that teleport the player to
5385        a place where another teleporter switch is in the same direction, which
5386        would then incorrectly be treated as immediately switched before the
5387        direction key that caused the switch was released) */
5388
5389     player->switch_x += jx - old_jx;
5390     player->switch_y += jy - old_jy;
5391   }
5392 }
5393
5394 static void Explode(int ex, int ey, int phase, int mode)
5395 {
5396   int x, y;
5397   int last_phase;
5398   int border_element;
5399
5400   // !!! eliminate this variable !!!
5401   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5402
5403   if (game.explosions_delayed)
5404   {
5405     ExplodeField[ex][ey] = mode;
5406     return;
5407   }
5408
5409   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5410   {
5411     int center_element = Feld[ex][ey];
5412     int artwork_element, explosion_element;     // set these values later
5413
5414     // remove things displayed in background while burning dynamite
5415     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5416       Back[ex][ey] = 0;
5417
5418     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5419     {
5420       // put moving element to center field (and let it explode there)
5421       center_element = MovingOrBlocked2Element(ex, ey);
5422       RemoveMovingField(ex, ey);
5423       Feld[ex][ey] = center_element;
5424     }
5425
5426     // now "center_element" is finally determined -- set related values now
5427     artwork_element = center_element;           // for custom player artwork
5428     explosion_element = center_element;         // for custom player artwork
5429
5430     if (IS_PLAYER(ex, ey))
5431     {
5432       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5433
5434       artwork_element = stored_player[player_nr].artwork_element;
5435
5436       if (level.use_explosion_element[player_nr])
5437       {
5438         explosion_element = level.explosion_element[player_nr];
5439         artwork_element = explosion_element;
5440       }
5441     }
5442
5443     if (mode == EX_TYPE_NORMAL ||
5444         mode == EX_TYPE_CENTER ||
5445         mode == EX_TYPE_CROSS)
5446       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5447
5448     last_phase = element_info[explosion_element].explosion_delay + 1;
5449
5450     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5451     {
5452       int xx = x - ex + 1;
5453       int yy = y - ey + 1;
5454       int element;
5455
5456       if (!IN_LEV_FIELD(x, y) ||
5457           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5458           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5459         continue;
5460
5461       element = Feld[x][y];
5462
5463       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5464       {
5465         element = MovingOrBlocked2Element(x, y);
5466
5467         if (!IS_EXPLOSION_PROOF(element))
5468           RemoveMovingField(x, y);
5469       }
5470
5471       // indestructible elements can only explode in center (but not flames)
5472       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5473                                            mode == EX_TYPE_BORDER)) ||
5474           element == EL_FLAMES)
5475         continue;
5476
5477       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5478          behaviour, for example when touching a yamyam that explodes to rocks
5479          with active deadly shield, a rock is created under the player !!! */
5480       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5481 #if 0
5482       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5483           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5484            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5485 #else
5486       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5487 #endif
5488       {
5489         if (IS_ACTIVE_BOMB(element))
5490         {
5491           // re-activate things under the bomb like gate or penguin
5492           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5493           Back[x][y] = 0;
5494         }
5495
5496         continue;
5497       }
5498
5499       // save walkable background elements while explosion on same tile
5500       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5501           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5502         Back[x][y] = element;
5503
5504       // ignite explodable elements reached by other explosion
5505       if (element == EL_EXPLOSION)
5506         element = Store2[x][y];
5507
5508       if (AmoebaNr[x][y] &&
5509           (element == EL_AMOEBA_FULL ||
5510            element == EL_BD_AMOEBA ||
5511            element == EL_AMOEBA_GROWING))
5512       {
5513         AmoebaCnt[AmoebaNr[x][y]]--;
5514         AmoebaCnt2[AmoebaNr[x][y]]--;
5515       }
5516
5517       RemoveField(x, y);
5518
5519       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5520       {
5521         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5522
5523         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5524
5525         if (PLAYERINFO(ex, ey)->use_murphy)
5526           Store[x][y] = EL_EMPTY;
5527       }
5528
5529       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5530       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5531       else if (ELEM_IS_PLAYER(center_element))
5532         Store[x][y] = EL_EMPTY;
5533       else if (center_element == EL_YAMYAM)
5534         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5535       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5536         Store[x][y] = element_info[center_element].content.e[xx][yy];
5537 #if 1
5538       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5539       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5540       // otherwise) -- FIX THIS !!!
5541       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5542         Store[x][y] = element_info[element].content.e[1][1];
5543 #else
5544       else if (!CAN_EXPLODE(element))
5545         Store[x][y] = element_info[element].content.e[1][1];
5546 #endif
5547       else
5548         Store[x][y] = EL_EMPTY;
5549
5550       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5551           center_element == EL_AMOEBA_TO_DIAMOND)
5552         Store2[x][y] = element;
5553
5554       Feld[x][y] = EL_EXPLOSION;
5555       GfxElement[x][y] = artwork_element;
5556
5557       ExplodePhase[x][y] = 1;
5558       ExplodeDelay[x][y] = last_phase;
5559
5560       Stop[x][y] = TRUE;
5561     }
5562
5563     if (center_element == EL_YAMYAM)
5564       game.yamyam_content_nr =
5565         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5566
5567     return;
5568   }
5569
5570   if (Stop[ex][ey])
5571     return;
5572
5573   x = ex;
5574   y = ey;
5575
5576   if (phase == 1)
5577     GfxFrame[x][y] = 0;         // restart explosion animation
5578
5579   last_phase = ExplodeDelay[x][y];
5580
5581   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5582
5583   // this can happen if the player leaves an explosion just in time
5584   if (GfxElement[x][y] == EL_UNDEFINED)
5585     GfxElement[x][y] = EL_EMPTY;
5586
5587   border_element = Store2[x][y];
5588   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5589     border_element = StorePlayer[x][y];
5590
5591   if (phase == element_info[border_element].ignition_delay ||
5592       phase == last_phase)
5593   {
5594     boolean border_explosion = FALSE;
5595
5596     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5597         !PLAYER_EXPLOSION_PROTECTED(x, y))
5598     {
5599       KillPlayerUnlessExplosionProtected(x, y);
5600       border_explosion = TRUE;
5601     }
5602     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5603     {
5604       Feld[x][y] = Store2[x][y];
5605       Store2[x][y] = 0;
5606       Bang(x, y);
5607       border_explosion = TRUE;
5608     }
5609     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5610     {
5611       AmoebeUmwandeln(x, y);
5612       Store2[x][y] = 0;
5613       border_explosion = TRUE;
5614     }
5615
5616     // if an element just explodes due to another explosion (chain-reaction),
5617     // do not immediately end the new explosion when it was the last frame of
5618     // the explosion (as it would be done in the following "if"-statement!)
5619     if (border_explosion && phase == last_phase)
5620       return;
5621   }
5622
5623   if (phase == last_phase)
5624   {
5625     int element;
5626
5627     element = Feld[x][y] = Store[x][y];
5628     Store[x][y] = Store2[x][y] = 0;
5629     GfxElement[x][y] = EL_UNDEFINED;
5630
5631     // player can escape from explosions and might therefore be still alive
5632     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5633         element <= EL_PLAYER_IS_EXPLODING_4)
5634     {
5635       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5636       int explosion_element = EL_PLAYER_1 + player_nr;
5637       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5638       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5639
5640       if (level.use_explosion_element[player_nr])
5641         explosion_element = level.explosion_element[player_nr];
5642
5643       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5644                     element_info[explosion_element].content.e[xx][yy]);
5645     }
5646
5647     // restore probably existing indestructible background element
5648     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5649       element = Feld[x][y] = Back[x][y];
5650     Back[x][y] = 0;
5651
5652     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5653     GfxDir[x][y] = MV_NONE;
5654     ChangeDelay[x][y] = 0;
5655     ChangePage[x][y] = -1;
5656
5657     CustomValue[x][y] = 0;
5658
5659     InitField_WithBug2(x, y, FALSE);
5660
5661     TEST_DrawLevelField(x, y);
5662
5663     TestIfElementTouchesCustomElement(x, y);
5664
5665     if (GFX_CRUMBLED(element))
5666       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5667
5668     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5669       StorePlayer[x][y] = 0;
5670
5671     if (ELEM_IS_PLAYER(element))
5672       RelocatePlayer(x, y, element);
5673   }
5674   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5675   {
5676     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5677     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5678
5679     if (phase == delay)
5680       TEST_DrawLevelFieldCrumbled(x, y);
5681
5682     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5683     {
5684       DrawLevelElement(x, y, Back[x][y]);
5685       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5686     }
5687     else if (IS_WALKABLE_UNDER(Back[x][y]))
5688     {
5689       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5690       DrawLevelElementThruMask(x, y, Back[x][y]);
5691     }
5692     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5693       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5694   }
5695 }
5696
5697 static void DynaExplode(int ex, int ey)
5698 {
5699   int i, j;
5700   int dynabomb_element = Feld[ex][ey];
5701   int dynabomb_size = 1;
5702   boolean dynabomb_xl = FALSE;
5703   struct PlayerInfo *player;
5704   static int xy[4][2] =
5705   {
5706     { 0, -1 },
5707     { -1, 0 },
5708     { +1, 0 },
5709     { 0, +1 }
5710   };
5711
5712   if (IS_ACTIVE_BOMB(dynabomb_element))
5713   {
5714     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5715     dynabomb_size = player->dynabomb_size;
5716     dynabomb_xl = player->dynabomb_xl;
5717     player->dynabombs_left++;
5718   }
5719
5720   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5721
5722   for (i = 0; i < NUM_DIRECTIONS; i++)
5723   {
5724     for (j = 1; j <= dynabomb_size; j++)
5725     {
5726       int x = ex + j * xy[i][0];
5727       int y = ey + j * xy[i][1];
5728       int element;
5729
5730       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5731         break;
5732
5733       element = Feld[x][y];
5734
5735       // do not restart explosions of fields with active bombs
5736       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5737         continue;
5738
5739       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5740
5741       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5742           !IS_DIGGABLE(element) && !dynabomb_xl)
5743         break;
5744     }
5745   }
5746 }
5747
5748 void Bang(int x, int y)
5749 {
5750   int element = MovingOrBlocked2Element(x, y);
5751   int explosion_type = EX_TYPE_NORMAL;
5752
5753   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5754   {
5755     struct PlayerInfo *player = PLAYERINFO(x, y);
5756
5757     element = Feld[x][y] = player->initial_element;
5758
5759     if (level.use_explosion_element[player->index_nr])
5760     {
5761       int explosion_element = level.explosion_element[player->index_nr];
5762
5763       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5764         explosion_type = EX_TYPE_CROSS;
5765       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5766         explosion_type = EX_TYPE_CENTER;
5767     }
5768   }
5769
5770   switch (element)
5771   {
5772     case EL_BUG:
5773     case EL_SPACESHIP:
5774     case EL_BD_BUTTERFLY:
5775     case EL_BD_FIREFLY:
5776     case EL_YAMYAM:
5777     case EL_DARK_YAMYAM:
5778     case EL_ROBOT:
5779     case EL_PACMAN:
5780     case EL_MOLE:
5781       RaiseScoreElement(element);
5782       break;
5783
5784     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5785     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5786     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5787     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5788     case EL_DYNABOMB_INCREASE_NUMBER:
5789     case EL_DYNABOMB_INCREASE_SIZE:
5790     case EL_DYNABOMB_INCREASE_POWER:
5791       explosion_type = EX_TYPE_DYNA;
5792       break;
5793
5794     case EL_DC_LANDMINE:
5795       explosion_type = EX_TYPE_CENTER;
5796       break;
5797
5798     case EL_PENGUIN:
5799     case EL_LAMP:
5800     case EL_LAMP_ACTIVE:
5801     case EL_AMOEBA_TO_DIAMOND:
5802       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5803         explosion_type = EX_TYPE_CENTER;
5804       break;
5805
5806     default:
5807       if (element_info[element].explosion_type == EXPLODES_CROSS)
5808         explosion_type = EX_TYPE_CROSS;
5809       else if (element_info[element].explosion_type == EXPLODES_1X1)
5810         explosion_type = EX_TYPE_CENTER;
5811       break;
5812   }
5813
5814   if (explosion_type == EX_TYPE_DYNA)
5815     DynaExplode(x, y);
5816   else
5817     Explode(x, y, EX_PHASE_START, explosion_type);
5818
5819   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5820 }
5821
5822 static void SplashAcid(int x, int y)
5823 {
5824   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5825       (!IN_LEV_FIELD(x - 1, y - 2) ||
5826        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5827     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5828
5829   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5830       (!IN_LEV_FIELD(x + 1, y - 2) ||
5831        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5832     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5833
5834   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5835 }
5836
5837 static void InitBeltMovement(void)
5838 {
5839   static int belt_base_element[4] =
5840   {
5841     EL_CONVEYOR_BELT_1_LEFT,
5842     EL_CONVEYOR_BELT_2_LEFT,
5843     EL_CONVEYOR_BELT_3_LEFT,
5844     EL_CONVEYOR_BELT_4_LEFT
5845   };
5846   static int belt_base_active_element[4] =
5847   {
5848     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5849     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5850     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5851     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5852   };
5853
5854   int x, y, i, j;
5855
5856   // set frame order for belt animation graphic according to belt direction
5857   for (i = 0; i < NUM_BELTS; i++)
5858   {
5859     int belt_nr = i;
5860
5861     for (j = 0; j < NUM_BELT_PARTS; j++)
5862     {
5863       int element = belt_base_active_element[belt_nr] + j;
5864       int graphic_1 = el2img(element);
5865       int graphic_2 = el2panelimg(element);
5866
5867       if (game.belt_dir[i] == MV_LEFT)
5868       {
5869         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5870         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5871       }
5872       else
5873       {
5874         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5875         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5876       }
5877     }
5878   }
5879
5880   SCAN_PLAYFIELD(x, y)
5881   {
5882     int element = Feld[x][y];
5883
5884     for (i = 0; i < NUM_BELTS; i++)
5885     {
5886       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5887       {
5888         int e_belt_nr = getBeltNrFromBeltElement(element);
5889         int belt_nr = i;
5890
5891         if (e_belt_nr == belt_nr)
5892         {
5893           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5894
5895           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5896         }
5897       }
5898     }
5899   }
5900 }
5901
5902 static void ToggleBeltSwitch(int x, int y)
5903 {
5904   static int belt_base_element[4] =
5905   {
5906     EL_CONVEYOR_BELT_1_LEFT,
5907     EL_CONVEYOR_BELT_2_LEFT,
5908     EL_CONVEYOR_BELT_3_LEFT,
5909     EL_CONVEYOR_BELT_4_LEFT
5910   };
5911   static int belt_base_active_element[4] =
5912   {
5913     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5914     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5915     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5916     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5917   };
5918   static int belt_base_switch_element[4] =
5919   {
5920     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5921     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5922     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5923     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5924   };
5925   static int belt_move_dir[4] =
5926   {
5927     MV_LEFT,
5928     MV_NONE,
5929     MV_RIGHT,
5930     MV_NONE,
5931   };
5932
5933   int element = Feld[x][y];
5934   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5935   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5936   int belt_dir = belt_move_dir[belt_dir_nr];
5937   int xx, yy, i;
5938
5939   if (!IS_BELT_SWITCH(element))
5940     return;
5941
5942   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5943   game.belt_dir[belt_nr] = belt_dir;
5944
5945   if (belt_dir_nr == 3)
5946     belt_dir_nr = 1;
5947
5948   // set frame order for belt animation graphic according to belt direction
5949   for (i = 0; i < NUM_BELT_PARTS; i++)
5950   {
5951     int element = belt_base_active_element[belt_nr] + i;
5952     int graphic_1 = el2img(element);
5953     int graphic_2 = el2panelimg(element);
5954
5955     if (belt_dir == MV_LEFT)
5956     {
5957       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5958       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5959     }
5960     else
5961     {
5962       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5963       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5964     }
5965   }
5966
5967   SCAN_PLAYFIELD(xx, yy)
5968   {
5969     int element = Feld[xx][yy];
5970
5971     if (IS_BELT_SWITCH(element))
5972     {
5973       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5974
5975       if (e_belt_nr == belt_nr)
5976       {
5977         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5978         TEST_DrawLevelField(xx, yy);
5979       }
5980     }
5981     else if (IS_BELT(element) && belt_dir != MV_NONE)
5982     {
5983       int e_belt_nr = getBeltNrFromBeltElement(element);
5984
5985       if (e_belt_nr == belt_nr)
5986       {
5987         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5988
5989         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5990         TEST_DrawLevelField(xx, yy);
5991       }
5992     }
5993     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5994     {
5995       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5996
5997       if (e_belt_nr == belt_nr)
5998       {
5999         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6000
6001         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6002         TEST_DrawLevelField(xx, yy);
6003       }
6004     }
6005   }
6006 }
6007
6008 static void ToggleSwitchgateSwitch(int x, int y)
6009 {
6010   int xx, yy;
6011
6012   game.switchgate_pos = !game.switchgate_pos;
6013
6014   SCAN_PLAYFIELD(xx, yy)
6015   {
6016     int element = Feld[xx][yy];
6017
6018     if (element == EL_SWITCHGATE_SWITCH_UP)
6019     {
6020       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6021       TEST_DrawLevelField(xx, yy);
6022     }
6023     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6024     {
6025       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6026       TEST_DrawLevelField(xx, yy);
6027     }
6028     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6029     {
6030       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6031       TEST_DrawLevelField(xx, yy);
6032     }
6033     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6034     {
6035       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6036       TEST_DrawLevelField(xx, yy);
6037     }
6038     else if (element == EL_SWITCHGATE_OPEN ||
6039              element == EL_SWITCHGATE_OPENING)
6040     {
6041       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6042
6043       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6044     }
6045     else if (element == EL_SWITCHGATE_CLOSED ||
6046              element == EL_SWITCHGATE_CLOSING)
6047     {
6048       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6049
6050       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6051     }
6052   }
6053 }
6054
6055 static int getInvisibleActiveFromInvisibleElement(int element)
6056 {
6057   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6058           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6059           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6060           element);
6061 }
6062
6063 static int getInvisibleFromInvisibleActiveElement(int element)
6064 {
6065   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6066           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6067           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6068           element);
6069 }
6070
6071 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6072 {
6073   int x, y;
6074
6075   SCAN_PLAYFIELD(x, y)
6076   {
6077     int element = Feld[x][y];
6078
6079     if (element == EL_LIGHT_SWITCH &&
6080         game.light_time_left > 0)
6081     {
6082       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6083       TEST_DrawLevelField(x, y);
6084     }
6085     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6086              game.light_time_left == 0)
6087     {
6088       Feld[x][y] = EL_LIGHT_SWITCH;
6089       TEST_DrawLevelField(x, y);
6090     }
6091     else if (element == EL_EMC_DRIPPER &&
6092              game.light_time_left > 0)
6093     {
6094       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6095       TEST_DrawLevelField(x, y);
6096     }
6097     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6098              game.light_time_left == 0)
6099     {
6100       Feld[x][y] = EL_EMC_DRIPPER;
6101       TEST_DrawLevelField(x, y);
6102     }
6103     else if (element == EL_INVISIBLE_STEELWALL ||
6104              element == EL_INVISIBLE_WALL ||
6105              element == EL_INVISIBLE_SAND)
6106     {
6107       if (game.light_time_left > 0)
6108         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6109
6110       TEST_DrawLevelField(x, y);
6111
6112       // uncrumble neighbour fields, if needed
6113       if (element == EL_INVISIBLE_SAND)
6114         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6115     }
6116     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6117              element == EL_INVISIBLE_WALL_ACTIVE ||
6118              element == EL_INVISIBLE_SAND_ACTIVE)
6119     {
6120       if (game.light_time_left == 0)
6121         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6122
6123       TEST_DrawLevelField(x, y);
6124
6125       // re-crumble neighbour fields, if needed
6126       if (element == EL_INVISIBLE_SAND)
6127         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6128     }
6129   }
6130 }
6131
6132 static void RedrawAllInvisibleElementsForLenses(void)
6133 {
6134   int x, y;
6135
6136   SCAN_PLAYFIELD(x, y)
6137   {
6138     int element = Feld[x][y];
6139
6140     if (element == EL_EMC_DRIPPER &&
6141         game.lenses_time_left > 0)
6142     {
6143       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6144       TEST_DrawLevelField(x, y);
6145     }
6146     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6147              game.lenses_time_left == 0)
6148     {
6149       Feld[x][y] = EL_EMC_DRIPPER;
6150       TEST_DrawLevelField(x, y);
6151     }
6152     else if (element == EL_INVISIBLE_STEELWALL ||
6153              element == EL_INVISIBLE_WALL ||
6154              element == EL_INVISIBLE_SAND)
6155     {
6156       if (game.lenses_time_left > 0)
6157         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6158
6159       TEST_DrawLevelField(x, y);
6160
6161       // uncrumble neighbour fields, if needed
6162       if (element == EL_INVISIBLE_SAND)
6163         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6164     }
6165     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6166              element == EL_INVISIBLE_WALL_ACTIVE ||
6167              element == EL_INVISIBLE_SAND_ACTIVE)
6168     {
6169       if (game.lenses_time_left == 0)
6170         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6171
6172       TEST_DrawLevelField(x, y);
6173
6174       // re-crumble neighbour fields, if needed
6175       if (element == EL_INVISIBLE_SAND)
6176         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6177     }
6178   }
6179 }
6180
6181 static void RedrawAllInvisibleElementsForMagnifier(void)
6182 {
6183   int x, y;
6184
6185   SCAN_PLAYFIELD(x, y)
6186   {
6187     int element = Feld[x][y];
6188
6189     if (element == EL_EMC_FAKE_GRASS &&
6190         game.magnify_time_left > 0)
6191     {
6192       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6193       TEST_DrawLevelField(x, y);
6194     }
6195     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6196              game.magnify_time_left == 0)
6197     {
6198       Feld[x][y] = EL_EMC_FAKE_GRASS;
6199       TEST_DrawLevelField(x, y);
6200     }
6201     else if (IS_GATE_GRAY(element) &&
6202              game.magnify_time_left > 0)
6203     {
6204       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6205                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6206                     IS_EM_GATE_GRAY(element) ?
6207                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6208                     IS_EMC_GATE_GRAY(element) ?
6209                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6210                     IS_DC_GATE_GRAY(element) ?
6211                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6212                     element);
6213       TEST_DrawLevelField(x, y);
6214     }
6215     else if (IS_GATE_GRAY_ACTIVE(element) &&
6216              game.magnify_time_left == 0)
6217     {
6218       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6219                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6220                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6221                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6222                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6223                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6224                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6225                     EL_DC_GATE_WHITE_GRAY :
6226                     element);
6227       TEST_DrawLevelField(x, y);
6228     }
6229   }
6230 }
6231
6232 static void ToggleLightSwitch(int x, int y)
6233 {
6234   int element = Feld[x][y];
6235
6236   game.light_time_left =
6237     (element == EL_LIGHT_SWITCH ?
6238      level.time_light * FRAMES_PER_SECOND : 0);
6239
6240   RedrawAllLightSwitchesAndInvisibleElements();
6241 }
6242
6243 static void ActivateTimegateSwitch(int x, int y)
6244 {
6245   int xx, yy;
6246
6247   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6248
6249   SCAN_PLAYFIELD(xx, yy)
6250   {
6251     int element = Feld[xx][yy];
6252
6253     if (element == EL_TIMEGATE_CLOSED ||
6254         element == EL_TIMEGATE_CLOSING)
6255     {
6256       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6257       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6258     }
6259
6260     /*
6261     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6262     {
6263       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6264       TEST_DrawLevelField(xx, yy);
6265     }
6266     */
6267
6268   }
6269
6270   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6271                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6272 }
6273
6274 static void Impact(int x, int y)
6275 {
6276   boolean last_line = (y == lev_fieldy - 1);
6277   boolean object_hit = FALSE;
6278   boolean impact = (last_line || object_hit);
6279   int element = Feld[x][y];
6280   int smashed = EL_STEELWALL;
6281
6282   if (!last_line)       // check if element below was hit
6283   {
6284     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6285       return;
6286
6287     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6288                                          MovDir[x][y + 1] != MV_DOWN ||
6289                                          MovPos[x][y + 1] <= TILEY / 2));
6290
6291     // do not smash moving elements that left the smashed field in time
6292     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6293         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6294       object_hit = FALSE;
6295
6296 #if USE_QUICKSAND_IMPACT_BUGFIX
6297     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6298     {
6299       RemoveMovingField(x, y + 1);
6300       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6301       Feld[x][y + 2] = EL_ROCK;
6302       TEST_DrawLevelField(x, y + 2);
6303
6304       object_hit = TRUE;
6305     }
6306
6307     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6308     {
6309       RemoveMovingField(x, y + 1);
6310       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6311       Feld[x][y + 2] = EL_ROCK;
6312       TEST_DrawLevelField(x, y + 2);
6313
6314       object_hit = TRUE;
6315     }
6316 #endif
6317
6318     if (object_hit)
6319       smashed = MovingOrBlocked2Element(x, y + 1);
6320
6321     impact = (last_line || object_hit);
6322   }
6323
6324   if (!last_line && smashed == EL_ACID) // element falls into acid
6325   {
6326     SplashAcid(x, y + 1);
6327     return;
6328   }
6329
6330   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6331   // only reset graphic animation if graphic really changes after impact
6332   if (impact &&
6333       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6334   {
6335     ResetGfxAnimation(x, y);
6336     TEST_DrawLevelField(x, y);
6337   }
6338
6339   if (impact && CAN_EXPLODE_IMPACT(element))
6340   {
6341     Bang(x, y);
6342     return;
6343   }
6344   else if (impact && element == EL_PEARL &&
6345            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6346   {
6347     ResetGfxAnimation(x, y);
6348
6349     Feld[x][y] = EL_PEARL_BREAKING;
6350     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6351     return;
6352   }
6353   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6354   {
6355     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6356
6357     return;
6358   }
6359
6360   if (impact && element == EL_AMOEBA_DROP)
6361   {
6362     if (object_hit && IS_PLAYER(x, y + 1))
6363       KillPlayerUnlessEnemyProtected(x, y + 1);
6364     else if (object_hit && smashed == EL_PENGUIN)
6365       Bang(x, y + 1);
6366     else
6367     {
6368       Feld[x][y] = EL_AMOEBA_GROWING;
6369       Store[x][y] = EL_AMOEBA_WET;
6370
6371       ResetRandomAnimationValue(x, y);
6372     }
6373     return;
6374   }
6375
6376   if (object_hit)               // check which object was hit
6377   {
6378     if ((CAN_PASS_MAGIC_WALL(element) && 
6379          (smashed == EL_MAGIC_WALL ||
6380           smashed == EL_BD_MAGIC_WALL)) ||
6381         (CAN_PASS_DC_MAGIC_WALL(element) &&
6382          smashed == EL_DC_MAGIC_WALL))
6383     {
6384       int xx, yy;
6385       int activated_magic_wall =
6386         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6387          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6388          EL_DC_MAGIC_WALL_ACTIVE);
6389
6390       // activate magic wall / mill
6391       SCAN_PLAYFIELD(xx, yy)
6392       {
6393         if (Feld[xx][yy] == smashed)
6394           Feld[xx][yy] = activated_magic_wall;
6395       }
6396
6397       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6398       game.magic_wall_active = TRUE;
6399
6400       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6401                             SND_MAGIC_WALL_ACTIVATING :
6402                             smashed == EL_BD_MAGIC_WALL ?
6403                             SND_BD_MAGIC_WALL_ACTIVATING :
6404                             SND_DC_MAGIC_WALL_ACTIVATING));
6405     }
6406
6407     if (IS_PLAYER(x, y + 1))
6408     {
6409       if (CAN_SMASH_PLAYER(element))
6410       {
6411         KillPlayerUnlessEnemyProtected(x, y + 1);
6412         return;
6413       }
6414     }
6415     else if (smashed == EL_PENGUIN)
6416     {
6417       if (CAN_SMASH_PLAYER(element))
6418       {
6419         Bang(x, y + 1);
6420         return;
6421       }
6422     }
6423     else if (element == EL_BD_DIAMOND)
6424     {
6425       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6426       {
6427         Bang(x, y + 1);
6428         return;
6429       }
6430     }
6431     else if (((element == EL_SP_INFOTRON ||
6432                element == EL_SP_ZONK) &&
6433               (smashed == EL_SP_SNIKSNAK ||
6434                smashed == EL_SP_ELECTRON ||
6435                smashed == EL_SP_DISK_ORANGE)) ||
6436              (element == EL_SP_INFOTRON &&
6437               smashed == EL_SP_DISK_YELLOW))
6438     {
6439       Bang(x, y + 1);
6440       return;
6441     }
6442     else if (CAN_SMASH_EVERYTHING(element))
6443     {
6444       if (IS_CLASSIC_ENEMY(smashed) ||
6445           CAN_EXPLODE_SMASHED(smashed))
6446       {
6447         Bang(x, y + 1);
6448         return;
6449       }
6450       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6451       {
6452         if (smashed == EL_LAMP ||
6453             smashed == EL_LAMP_ACTIVE)
6454         {
6455           Bang(x, y + 1);
6456           return;
6457         }
6458         else if (smashed == EL_NUT)
6459         {
6460           Feld[x][y + 1] = EL_NUT_BREAKING;
6461           PlayLevelSound(x, y, SND_NUT_BREAKING);
6462           RaiseScoreElement(EL_NUT);
6463           return;
6464         }
6465         else if (smashed == EL_PEARL)
6466         {
6467           ResetGfxAnimation(x, y);
6468
6469           Feld[x][y + 1] = EL_PEARL_BREAKING;
6470           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6471           return;
6472         }
6473         else if (smashed == EL_DIAMOND)
6474         {
6475           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6476           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6477           return;
6478         }
6479         else if (IS_BELT_SWITCH(smashed))
6480         {
6481           ToggleBeltSwitch(x, y + 1);
6482         }
6483         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6484                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6485                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6486                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6487         {
6488           ToggleSwitchgateSwitch(x, y + 1);
6489         }
6490         else if (smashed == EL_LIGHT_SWITCH ||
6491                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6492         {
6493           ToggleLightSwitch(x, y + 1);
6494         }
6495         else
6496         {
6497           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6498
6499           CheckElementChangeBySide(x, y + 1, smashed, element,
6500                                    CE_SWITCHED, CH_SIDE_TOP);
6501           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6502                                             CH_SIDE_TOP);
6503         }
6504       }
6505       else
6506       {
6507         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6508       }
6509     }
6510   }
6511
6512   // play sound of magic wall / mill
6513   if (!last_line &&
6514       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6515        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6516        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6517   {
6518     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6519       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6520     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6521       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6522     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6523       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6524
6525     return;
6526   }
6527
6528   // play sound of object that hits the ground
6529   if (last_line || object_hit)
6530     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6531 }
6532
6533 static void TurnRoundExt(int x, int y)
6534 {
6535   static struct
6536   {
6537     int dx, dy;
6538   } move_xy[] =
6539   {
6540     {  0,  0 },
6541     { -1,  0 },
6542     { +1,  0 },
6543     {  0,  0 },
6544     {  0, -1 },
6545     {  0,  0 }, { 0, 0 }, { 0, 0 },
6546     {  0, +1 }
6547   };
6548   static struct
6549   {
6550     int left, right, back;
6551   } turn[] =
6552   {
6553     { 0,        0,              0        },
6554     { MV_DOWN,  MV_UP,          MV_RIGHT },
6555     { MV_UP,    MV_DOWN,        MV_LEFT  },
6556     { 0,        0,              0        },
6557     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6558     { 0,        0,              0        },
6559     { 0,        0,              0        },
6560     { 0,        0,              0        },
6561     { MV_RIGHT, MV_LEFT,        MV_UP    }
6562   };
6563
6564   int element = Feld[x][y];
6565   int move_pattern = element_info[element].move_pattern;
6566
6567   int old_move_dir = MovDir[x][y];
6568   int left_dir  = turn[old_move_dir].left;
6569   int right_dir = turn[old_move_dir].right;
6570   int back_dir  = turn[old_move_dir].back;
6571
6572   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6573   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6574   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6575   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6576
6577   int left_x  = x + left_dx,  left_y  = y + left_dy;
6578   int right_x = x + right_dx, right_y = y + right_dy;
6579   int move_x  = x + move_dx,  move_y  = y + move_dy;
6580
6581   int xx, yy;
6582
6583   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6584   {
6585     TestIfBadThingTouchesOtherBadThing(x, y);
6586
6587     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6588       MovDir[x][y] = right_dir;
6589     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6590       MovDir[x][y] = left_dir;
6591
6592     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6593       MovDelay[x][y] = 9;
6594     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6595       MovDelay[x][y] = 1;
6596   }
6597   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6598   {
6599     TestIfBadThingTouchesOtherBadThing(x, y);
6600
6601     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6602       MovDir[x][y] = left_dir;
6603     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6604       MovDir[x][y] = right_dir;
6605
6606     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6607       MovDelay[x][y] = 9;
6608     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6609       MovDelay[x][y] = 1;
6610   }
6611   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6612   {
6613     TestIfBadThingTouchesOtherBadThing(x, y);
6614
6615     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6616       MovDir[x][y] = left_dir;
6617     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6618       MovDir[x][y] = right_dir;
6619
6620     if (MovDir[x][y] != old_move_dir)
6621       MovDelay[x][y] = 9;
6622   }
6623   else if (element == EL_YAMYAM)
6624   {
6625     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6626     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6627
6628     if (can_turn_left && can_turn_right)
6629       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6630     else if (can_turn_left)
6631       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6632     else if (can_turn_right)
6633       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6634     else
6635       MovDir[x][y] = back_dir;
6636
6637     MovDelay[x][y] = 16 + 16 * RND(3);
6638   }
6639   else if (element == EL_DARK_YAMYAM)
6640   {
6641     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6642                                                          left_x, left_y);
6643     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6644                                                          right_x, right_y);
6645
6646     if (can_turn_left && can_turn_right)
6647       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6648     else if (can_turn_left)
6649       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6650     else if (can_turn_right)
6651       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6652     else
6653       MovDir[x][y] = back_dir;
6654
6655     MovDelay[x][y] = 16 + 16 * RND(3);
6656   }
6657   else if (element == EL_PACMAN)
6658   {
6659     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6660     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6661
6662     if (can_turn_left && can_turn_right)
6663       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6664     else if (can_turn_left)
6665       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6666     else if (can_turn_right)
6667       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6668     else
6669       MovDir[x][y] = back_dir;
6670
6671     MovDelay[x][y] = 6 + RND(40);
6672   }
6673   else if (element == EL_PIG)
6674   {
6675     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6676     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6677     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6678     boolean should_turn_left, should_turn_right, should_move_on;
6679     int rnd_value = 24;
6680     int rnd = RND(rnd_value);
6681
6682     should_turn_left = (can_turn_left &&
6683                         (!can_move_on ||
6684                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6685                                                    y + back_dy + left_dy)));
6686     should_turn_right = (can_turn_right &&
6687                          (!can_move_on ||
6688                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6689                                                     y + back_dy + right_dy)));
6690     should_move_on = (can_move_on &&
6691                       (!can_turn_left ||
6692                        !can_turn_right ||
6693                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6694                                                  y + move_dy + left_dy) ||
6695                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6696                                                  y + move_dy + right_dy)));
6697
6698     if (should_turn_left || should_turn_right || should_move_on)
6699     {
6700       if (should_turn_left && should_turn_right && should_move_on)
6701         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6702                         rnd < 2 * rnd_value / 3 ? right_dir :
6703                         old_move_dir);
6704       else if (should_turn_left && should_turn_right)
6705         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6706       else if (should_turn_left && should_move_on)
6707         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6708       else if (should_turn_right && should_move_on)
6709         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6710       else if (should_turn_left)
6711         MovDir[x][y] = left_dir;
6712       else if (should_turn_right)
6713         MovDir[x][y] = right_dir;
6714       else if (should_move_on)
6715         MovDir[x][y] = old_move_dir;
6716     }
6717     else if (can_move_on && rnd > rnd_value / 8)
6718       MovDir[x][y] = old_move_dir;
6719     else if (can_turn_left && can_turn_right)
6720       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6721     else if (can_turn_left && rnd > rnd_value / 8)
6722       MovDir[x][y] = left_dir;
6723     else if (can_turn_right && rnd > rnd_value/8)
6724       MovDir[x][y] = right_dir;
6725     else
6726       MovDir[x][y] = back_dir;
6727
6728     xx = x + move_xy[MovDir[x][y]].dx;
6729     yy = y + move_xy[MovDir[x][y]].dy;
6730
6731     if (!IN_LEV_FIELD(xx, yy) ||
6732         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6733       MovDir[x][y] = old_move_dir;
6734
6735     MovDelay[x][y] = 0;
6736   }
6737   else if (element == EL_DRAGON)
6738   {
6739     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6740     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6741     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6742     int rnd_value = 24;
6743     int rnd = RND(rnd_value);
6744
6745     if (can_move_on && rnd > rnd_value / 8)
6746       MovDir[x][y] = old_move_dir;
6747     else if (can_turn_left && can_turn_right)
6748       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6749     else if (can_turn_left && rnd > rnd_value / 8)
6750       MovDir[x][y] = left_dir;
6751     else if (can_turn_right && rnd > rnd_value / 8)
6752       MovDir[x][y] = right_dir;
6753     else
6754       MovDir[x][y] = back_dir;
6755
6756     xx = x + move_xy[MovDir[x][y]].dx;
6757     yy = y + move_xy[MovDir[x][y]].dy;
6758
6759     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6760       MovDir[x][y] = old_move_dir;
6761
6762     MovDelay[x][y] = 0;
6763   }
6764   else if (element == EL_MOLE)
6765   {
6766     boolean can_move_on =
6767       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6768                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6769                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6770     if (!can_move_on)
6771     {
6772       boolean can_turn_left =
6773         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6774                               IS_AMOEBOID(Feld[left_x][left_y])));
6775
6776       boolean can_turn_right =
6777         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6778                               IS_AMOEBOID(Feld[right_x][right_y])));
6779
6780       if (can_turn_left && can_turn_right)
6781         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6782       else if (can_turn_left)
6783         MovDir[x][y] = left_dir;
6784       else
6785         MovDir[x][y] = right_dir;
6786     }
6787
6788     if (MovDir[x][y] != old_move_dir)
6789       MovDelay[x][y] = 9;
6790   }
6791   else if (element == EL_BALLOON)
6792   {
6793     MovDir[x][y] = game.wind_direction;
6794     MovDelay[x][y] = 0;
6795   }
6796   else if (element == EL_SPRING)
6797   {
6798     if (MovDir[x][y] & MV_HORIZONTAL)
6799     {
6800       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6801           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6802       {
6803         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6804         ResetGfxAnimation(move_x, move_y);
6805         TEST_DrawLevelField(move_x, move_y);
6806
6807         MovDir[x][y] = back_dir;
6808       }
6809       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6810                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6811         MovDir[x][y] = MV_NONE;
6812     }
6813
6814     MovDelay[x][y] = 0;
6815   }
6816   else if (element == EL_ROBOT ||
6817            element == EL_SATELLITE ||
6818            element == EL_PENGUIN ||
6819            element == EL_EMC_ANDROID)
6820   {
6821     int attr_x = -1, attr_y = -1;
6822
6823     if (game.all_players_gone)
6824     {
6825       attr_x = game.exit_x;
6826       attr_y = game.exit_y;
6827     }
6828     else
6829     {
6830       int i;
6831
6832       for (i = 0; i < MAX_PLAYERS; i++)
6833       {
6834         struct PlayerInfo *player = &stored_player[i];
6835         int jx = player->jx, jy = player->jy;
6836
6837         if (!player->active)
6838           continue;
6839
6840         if (attr_x == -1 ||
6841             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6842         {
6843           attr_x = jx;
6844           attr_y = jy;
6845         }
6846       }
6847     }
6848
6849     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6850         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6851          game.engine_version < VERSION_IDENT(3,1,0,0)))
6852     {
6853       attr_x = ZX;
6854       attr_y = ZY;
6855     }
6856
6857     if (element == EL_PENGUIN)
6858     {
6859       int i;
6860       static int xy[4][2] =
6861       {
6862         { 0, -1 },
6863         { -1, 0 },
6864         { +1, 0 },
6865         { 0, +1 }
6866       };
6867
6868       for (i = 0; i < NUM_DIRECTIONS; i++)
6869       {
6870         int ex = x + xy[i][0];
6871         int ey = y + xy[i][1];
6872
6873         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6874                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6875                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6876                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6877         {
6878           attr_x = ex;
6879           attr_y = ey;
6880           break;
6881         }
6882       }
6883     }
6884
6885     MovDir[x][y] = MV_NONE;
6886     if (attr_x < x)
6887       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6888     else if (attr_x > x)
6889       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6890     if (attr_y < y)
6891       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6892     else if (attr_y > y)
6893       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6894
6895     if (element == EL_ROBOT)
6896     {
6897       int newx, newy;
6898
6899       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6900         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6901       Moving2Blocked(x, y, &newx, &newy);
6902
6903       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6904         MovDelay[x][y] = 8 + 8 * !RND(3);
6905       else
6906         MovDelay[x][y] = 16;
6907     }
6908     else if (element == EL_PENGUIN)
6909     {
6910       int newx, newy;
6911
6912       MovDelay[x][y] = 1;
6913
6914       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6915       {
6916         boolean first_horiz = RND(2);
6917         int new_move_dir = MovDir[x][y];
6918
6919         MovDir[x][y] =
6920           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6921         Moving2Blocked(x, y, &newx, &newy);
6922
6923         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6924           return;
6925
6926         MovDir[x][y] =
6927           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6928         Moving2Blocked(x, y, &newx, &newy);
6929
6930         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6931           return;
6932
6933         MovDir[x][y] = old_move_dir;
6934         return;
6935       }
6936     }
6937     else if (element == EL_SATELLITE)
6938     {
6939       int newx, newy;
6940
6941       MovDelay[x][y] = 1;
6942
6943       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6944       {
6945         boolean first_horiz = RND(2);
6946         int new_move_dir = MovDir[x][y];
6947
6948         MovDir[x][y] =
6949           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6950         Moving2Blocked(x, y, &newx, &newy);
6951
6952         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6953           return;
6954
6955         MovDir[x][y] =
6956           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6957         Moving2Blocked(x, y, &newx, &newy);
6958
6959         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6960           return;
6961
6962         MovDir[x][y] = old_move_dir;
6963         return;
6964       }
6965     }
6966     else if (element == EL_EMC_ANDROID)
6967     {
6968       static int check_pos[16] =
6969       {
6970         -1,             //  0 => (invalid)
6971         7,              //  1 => MV_LEFT
6972         3,              //  2 => MV_RIGHT
6973         -1,             //  3 => (invalid)
6974         1,              //  4 =>            MV_UP
6975         0,              //  5 => MV_LEFT  | MV_UP
6976         2,              //  6 => MV_RIGHT | MV_UP
6977         -1,             //  7 => (invalid)
6978         5,              //  8 =>            MV_DOWN
6979         6,              //  9 => MV_LEFT  | MV_DOWN
6980         4,              // 10 => MV_RIGHT | MV_DOWN
6981         -1,             // 11 => (invalid)
6982         -1,             // 12 => (invalid)
6983         -1,             // 13 => (invalid)
6984         -1,             // 14 => (invalid)
6985         -1,             // 15 => (invalid)
6986       };
6987       static struct
6988       {
6989         int dx, dy;
6990         int dir;
6991       } check_xy[8] =
6992       {
6993         { -1, -1,       MV_LEFT  | MV_UP   },
6994         {  0, -1,                  MV_UP   },
6995         { +1, -1,       MV_RIGHT | MV_UP   },
6996         { +1,  0,       MV_RIGHT           },
6997         { +1, +1,       MV_RIGHT | MV_DOWN },
6998         {  0, +1,                  MV_DOWN },
6999         { -1, +1,       MV_LEFT  | MV_DOWN },
7000         { -1,  0,       MV_LEFT            },
7001       };
7002       int start_pos, check_order;
7003       boolean can_clone = FALSE;
7004       int i;
7005
7006       // check if there is any free field around current position
7007       for (i = 0; i < 8; i++)
7008       {
7009         int newx = x + check_xy[i].dx;
7010         int newy = y + check_xy[i].dy;
7011
7012         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7013         {
7014           can_clone = TRUE;
7015
7016           break;
7017         }
7018       }
7019
7020       if (can_clone)            // randomly find an element to clone
7021       {
7022         can_clone = FALSE;
7023
7024         start_pos = check_pos[RND(8)];
7025         check_order = (RND(2) ? -1 : +1);
7026
7027         for (i = 0; i < 8; i++)
7028         {
7029           int pos_raw = start_pos + i * check_order;
7030           int pos = (pos_raw + 8) % 8;
7031           int newx = x + check_xy[pos].dx;
7032           int newy = y + check_xy[pos].dy;
7033
7034           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7035           {
7036             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7037             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7038
7039             Store[x][y] = Feld[newx][newy];
7040
7041             can_clone = TRUE;
7042
7043             break;
7044           }
7045         }
7046       }
7047
7048       if (can_clone)            // randomly find a direction to move
7049       {
7050         can_clone = FALSE;
7051
7052         start_pos = check_pos[RND(8)];
7053         check_order = (RND(2) ? -1 : +1);
7054
7055         for (i = 0; i < 8; i++)
7056         {
7057           int pos_raw = start_pos + i * check_order;
7058           int pos = (pos_raw + 8) % 8;
7059           int newx = x + check_xy[pos].dx;
7060           int newy = y + check_xy[pos].dy;
7061           int new_move_dir = check_xy[pos].dir;
7062
7063           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7064           {
7065             MovDir[x][y] = new_move_dir;
7066             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7067
7068             can_clone = TRUE;
7069
7070             break;
7071           }
7072         }
7073       }
7074
7075       if (can_clone)            // cloning and moving successful
7076         return;
7077
7078       // cannot clone -- try to move towards player
7079
7080       start_pos = check_pos[MovDir[x][y] & 0x0f];
7081       check_order = (RND(2) ? -1 : +1);
7082
7083       for (i = 0; i < 3; i++)
7084       {
7085         // first check start_pos, then previous/next or (next/previous) pos
7086         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7087         int pos = (pos_raw + 8) % 8;
7088         int newx = x + check_xy[pos].dx;
7089         int newy = y + check_xy[pos].dy;
7090         int new_move_dir = check_xy[pos].dir;
7091
7092         if (IS_PLAYER(newx, newy))
7093           break;
7094
7095         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7096         {
7097           MovDir[x][y] = new_move_dir;
7098           MovDelay[x][y] = level.android_move_time * 8 + 1;
7099
7100           break;
7101         }
7102       }
7103     }
7104   }
7105   else if (move_pattern == MV_TURNING_LEFT ||
7106            move_pattern == MV_TURNING_RIGHT ||
7107            move_pattern == MV_TURNING_LEFT_RIGHT ||
7108            move_pattern == MV_TURNING_RIGHT_LEFT ||
7109            move_pattern == MV_TURNING_RANDOM ||
7110            move_pattern == MV_ALL_DIRECTIONS)
7111   {
7112     boolean can_turn_left =
7113       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7114     boolean can_turn_right =
7115       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7116
7117     if (element_info[element].move_stepsize == 0)       // "not moving"
7118       return;
7119
7120     if (move_pattern == MV_TURNING_LEFT)
7121       MovDir[x][y] = left_dir;
7122     else if (move_pattern == MV_TURNING_RIGHT)
7123       MovDir[x][y] = right_dir;
7124     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7125       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7126     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7127       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7128     else if (move_pattern == MV_TURNING_RANDOM)
7129       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7130                       can_turn_right && !can_turn_left ? right_dir :
7131                       RND(2) ? left_dir : right_dir);
7132     else if (can_turn_left && can_turn_right)
7133       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7134     else if (can_turn_left)
7135       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7136     else if (can_turn_right)
7137       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7138     else
7139       MovDir[x][y] = back_dir;
7140
7141     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7142   }
7143   else if (move_pattern == MV_HORIZONTAL ||
7144            move_pattern == MV_VERTICAL)
7145   {
7146     if (move_pattern & old_move_dir)
7147       MovDir[x][y] = back_dir;
7148     else if (move_pattern == MV_HORIZONTAL)
7149       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7150     else if (move_pattern == MV_VERTICAL)
7151       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7152
7153     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7154   }
7155   else if (move_pattern & MV_ANY_DIRECTION)
7156   {
7157     MovDir[x][y] = move_pattern;
7158     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7159   }
7160   else if (move_pattern & MV_WIND_DIRECTION)
7161   {
7162     MovDir[x][y] = game.wind_direction;
7163     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7164   }
7165   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7166   {
7167     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7168       MovDir[x][y] = left_dir;
7169     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7170       MovDir[x][y] = right_dir;
7171
7172     if (MovDir[x][y] != old_move_dir)
7173       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7174   }
7175   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7176   {
7177     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7178       MovDir[x][y] = right_dir;
7179     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7180       MovDir[x][y] = left_dir;
7181
7182     if (MovDir[x][y] != old_move_dir)
7183       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7184   }
7185   else if (move_pattern == MV_TOWARDS_PLAYER ||
7186            move_pattern == MV_AWAY_FROM_PLAYER)
7187   {
7188     int attr_x = -1, attr_y = -1;
7189     int newx, newy;
7190     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7191
7192     if (game.all_players_gone)
7193     {
7194       attr_x = game.exit_x;
7195       attr_y = game.exit_y;
7196     }
7197     else
7198     {
7199       int i;
7200
7201       for (i = 0; i < MAX_PLAYERS; i++)
7202       {
7203         struct PlayerInfo *player = &stored_player[i];
7204         int jx = player->jx, jy = player->jy;
7205
7206         if (!player->active)
7207           continue;
7208
7209         if (attr_x == -1 ||
7210             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7211         {
7212           attr_x = jx;
7213           attr_y = jy;
7214         }
7215       }
7216     }
7217
7218     MovDir[x][y] = MV_NONE;
7219     if (attr_x < x)
7220       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7221     else if (attr_x > x)
7222       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7223     if (attr_y < y)
7224       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7225     else if (attr_y > y)
7226       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7227
7228     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7229
7230     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7231     {
7232       boolean first_horiz = RND(2);
7233       int new_move_dir = MovDir[x][y];
7234
7235       if (element_info[element].move_stepsize == 0)     // "not moving"
7236       {
7237         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7238         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7239
7240         return;
7241       }
7242
7243       MovDir[x][y] =
7244         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7245       Moving2Blocked(x, y, &newx, &newy);
7246
7247       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7248         return;
7249
7250       MovDir[x][y] =
7251         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7252       Moving2Blocked(x, y, &newx, &newy);
7253
7254       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7255         return;
7256
7257       MovDir[x][y] = old_move_dir;
7258     }
7259   }
7260   else if (move_pattern == MV_WHEN_PUSHED ||
7261            move_pattern == MV_WHEN_DROPPED)
7262   {
7263     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7264       MovDir[x][y] = MV_NONE;
7265
7266     MovDelay[x][y] = 0;
7267   }
7268   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7269   {
7270     static int test_xy[7][2] =
7271     {
7272       { 0, -1 },
7273       { -1, 0 },
7274       { +1, 0 },
7275       { 0, +1 },
7276       { 0, -1 },
7277       { -1, 0 },
7278       { +1, 0 },
7279     };
7280     static int test_dir[7] =
7281     {
7282       MV_UP,
7283       MV_LEFT,
7284       MV_RIGHT,
7285       MV_DOWN,
7286       MV_UP,
7287       MV_LEFT,
7288       MV_RIGHT,
7289     };
7290     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7291     int move_preference = -1000000;     // start with very low preference
7292     int new_move_dir = MV_NONE;
7293     int start_test = RND(4);
7294     int i;
7295
7296     for (i = 0; i < NUM_DIRECTIONS; i++)
7297     {
7298       int move_dir = test_dir[start_test + i];
7299       int move_dir_preference;
7300
7301       xx = x + test_xy[start_test + i][0];
7302       yy = y + test_xy[start_test + i][1];
7303
7304       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7305           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7306       {
7307         new_move_dir = move_dir;
7308
7309         break;
7310       }
7311
7312       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7313         continue;
7314
7315       move_dir_preference = -1 * RunnerVisit[xx][yy];
7316       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7317         move_dir_preference = PlayerVisit[xx][yy];
7318
7319       if (move_dir_preference > move_preference)
7320       {
7321         // prefer field that has not been visited for the longest time
7322         move_preference = move_dir_preference;
7323         new_move_dir = move_dir;
7324       }
7325       else if (move_dir_preference == move_preference &&
7326                move_dir == old_move_dir)
7327       {
7328         // prefer last direction when all directions are preferred equally
7329         move_preference = move_dir_preference;
7330         new_move_dir = move_dir;
7331       }
7332     }
7333
7334     MovDir[x][y] = new_move_dir;
7335     if (old_move_dir != new_move_dir)
7336       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7337   }
7338 }
7339
7340 static void TurnRound(int x, int y)
7341 {
7342   int direction = MovDir[x][y];
7343
7344   TurnRoundExt(x, y);
7345
7346   GfxDir[x][y] = MovDir[x][y];
7347
7348   if (direction != MovDir[x][y])
7349     GfxFrame[x][y] = 0;
7350
7351   if (MovDelay[x][y])
7352     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7353
7354   ResetGfxFrame(x, y);
7355 }
7356
7357 static boolean JustBeingPushed(int x, int y)
7358 {
7359   int i;
7360
7361   for (i = 0; i < MAX_PLAYERS; i++)
7362   {
7363     struct PlayerInfo *player = &stored_player[i];
7364
7365     if (player->active && player->is_pushing && player->MovPos)
7366     {
7367       int next_jx = player->jx + (player->jx - player->last_jx);
7368       int next_jy = player->jy + (player->jy - player->last_jy);
7369
7370       if (x == next_jx && y == next_jy)
7371         return TRUE;
7372     }
7373   }
7374
7375   return FALSE;
7376 }
7377
7378 static void StartMoving(int x, int y)
7379 {
7380   boolean started_moving = FALSE;       // some elements can fall _and_ move
7381   int element = Feld[x][y];
7382
7383   if (Stop[x][y])
7384     return;
7385
7386   if (MovDelay[x][y] == 0)
7387     GfxAction[x][y] = ACTION_DEFAULT;
7388
7389   if (CAN_FALL(element) && y < lev_fieldy - 1)
7390   {
7391     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7392         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7393       if (JustBeingPushed(x, y))
7394         return;
7395
7396     if (element == EL_QUICKSAND_FULL)
7397     {
7398       if (IS_FREE(x, y + 1))
7399       {
7400         InitMovingField(x, y, MV_DOWN);
7401         started_moving = TRUE;
7402
7403         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7404 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7405         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7406           Store[x][y] = EL_ROCK;
7407 #else
7408         Store[x][y] = EL_ROCK;
7409 #endif
7410
7411         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7412       }
7413       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7414       {
7415         if (!MovDelay[x][y])
7416         {
7417           MovDelay[x][y] = TILEY + 1;
7418
7419           ResetGfxAnimation(x, y);
7420           ResetGfxAnimation(x, y + 1);
7421         }
7422
7423         if (MovDelay[x][y])
7424         {
7425           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7426           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7427
7428           MovDelay[x][y]--;
7429           if (MovDelay[x][y])
7430             return;
7431         }
7432
7433         Feld[x][y] = EL_QUICKSAND_EMPTY;
7434         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7435         Store[x][y + 1] = Store[x][y];
7436         Store[x][y] = 0;
7437
7438         PlayLevelSoundAction(x, y, ACTION_FILLING);
7439       }
7440       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7441       {
7442         if (!MovDelay[x][y])
7443         {
7444           MovDelay[x][y] = TILEY + 1;
7445
7446           ResetGfxAnimation(x, y);
7447           ResetGfxAnimation(x, y + 1);
7448         }
7449
7450         if (MovDelay[x][y])
7451         {
7452           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7453           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7454
7455           MovDelay[x][y]--;
7456           if (MovDelay[x][y])
7457             return;
7458         }
7459
7460         Feld[x][y] = EL_QUICKSAND_EMPTY;
7461         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7462         Store[x][y + 1] = Store[x][y];
7463         Store[x][y] = 0;
7464
7465         PlayLevelSoundAction(x, y, ACTION_FILLING);
7466       }
7467     }
7468     else if (element == EL_QUICKSAND_FAST_FULL)
7469     {
7470       if (IS_FREE(x, y + 1))
7471       {
7472         InitMovingField(x, y, MV_DOWN);
7473         started_moving = TRUE;
7474
7475         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7476 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7477         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7478           Store[x][y] = EL_ROCK;
7479 #else
7480         Store[x][y] = EL_ROCK;
7481 #endif
7482
7483         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7484       }
7485       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7486       {
7487         if (!MovDelay[x][y])
7488         {
7489           MovDelay[x][y] = TILEY + 1;
7490
7491           ResetGfxAnimation(x, y);
7492           ResetGfxAnimation(x, y + 1);
7493         }
7494
7495         if (MovDelay[x][y])
7496         {
7497           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7498           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7499
7500           MovDelay[x][y]--;
7501           if (MovDelay[x][y])
7502             return;
7503         }
7504
7505         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7506         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7507         Store[x][y + 1] = Store[x][y];
7508         Store[x][y] = 0;
7509
7510         PlayLevelSoundAction(x, y, ACTION_FILLING);
7511       }
7512       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7513       {
7514         if (!MovDelay[x][y])
7515         {
7516           MovDelay[x][y] = TILEY + 1;
7517
7518           ResetGfxAnimation(x, y);
7519           ResetGfxAnimation(x, y + 1);
7520         }
7521
7522         if (MovDelay[x][y])
7523         {
7524           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7525           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7526
7527           MovDelay[x][y]--;
7528           if (MovDelay[x][y])
7529             return;
7530         }
7531
7532         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7533         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7534         Store[x][y + 1] = Store[x][y];
7535         Store[x][y] = 0;
7536
7537         PlayLevelSoundAction(x, y, ACTION_FILLING);
7538       }
7539     }
7540     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7541              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7542     {
7543       InitMovingField(x, y, MV_DOWN);
7544       started_moving = TRUE;
7545
7546       Feld[x][y] = EL_QUICKSAND_FILLING;
7547       Store[x][y] = element;
7548
7549       PlayLevelSoundAction(x, y, ACTION_FILLING);
7550     }
7551     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7552              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7553     {
7554       InitMovingField(x, y, MV_DOWN);
7555       started_moving = TRUE;
7556
7557       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7558       Store[x][y] = element;
7559
7560       PlayLevelSoundAction(x, y, ACTION_FILLING);
7561     }
7562     else if (element == EL_MAGIC_WALL_FULL)
7563     {
7564       if (IS_FREE(x, y + 1))
7565       {
7566         InitMovingField(x, y, MV_DOWN);
7567         started_moving = TRUE;
7568
7569         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7570         Store[x][y] = EL_CHANGED(Store[x][y]);
7571       }
7572       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7573       {
7574         if (!MovDelay[x][y])
7575           MovDelay[x][y] = TILEY / 4 + 1;
7576
7577         if (MovDelay[x][y])
7578         {
7579           MovDelay[x][y]--;
7580           if (MovDelay[x][y])
7581             return;
7582         }
7583
7584         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7585         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7586         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7587         Store[x][y] = 0;
7588       }
7589     }
7590     else if (element == EL_BD_MAGIC_WALL_FULL)
7591     {
7592       if (IS_FREE(x, y + 1))
7593       {
7594         InitMovingField(x, y, MV_DOWN);
7595         started_moving = TRUE;
7596
7597         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7598         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7599       }
7600       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7601       {
7602         if (!MovDelay[x][y])
7603           MovDelay[x][y] = TILEY / 4 + 1;
7604
7605         if (MovDelay[x][y])
7606         {
7607           MovDelay[x][y]--;
7608           if (MovDelay[x][y])
7609             return;
7610         }
7611
7612         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7613         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7614         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7615         Store[x][y] = 0;
7616       }
7617     }
7618     else if (element == EL_DC_MAGIC_WALL_FULL)
7619     {
7620       if (IS_FREE(x, y + 1))
7621       {
7622         InitMovingField(x, y, MV_DOWN);
7623         started_moving = TRUE;
7624
7625         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7626         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7627       }
7628       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7629       {
7630         if (!MovDelay[x][y])
7631           MovDelay[x][y] = TILEY / 4 + 1;
7632
7633         if (MovDelay[x][y])
7634         {
7635           MovDelay[x][y]--;
7636           if (MovDelay[x][y])
7637             return;
7638         }
7639
7640         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7641         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7642         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7643         Store[x][y] = 0;
7644       }
7645     }
7646     else if ((CAN_PASS_MAGIC_WALL(element) &&
7647               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7648                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7649              (CAN_PASS_DC_MAGIC_WALL(element) &&
7650               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7651
7652     {
7653       InitMovingField(x, y, MV_DOWN);
7654       started_moving = TRUE;
7655
7656       Feld[x][y] =
7657         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7658          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7659          EL_DC_MAGIC_WALL_FILLING);
7660       Store[x][y] = element;
7661     }
7662     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7663     {
7664       SplashAcid(x, y + 1);
7665
7666       InitMovingField(x, y, MV_DOWN);
7667       started_moving = TRUE;
7668
7669       Store[x][y] = EL_ACID;
7670     }
7671     else if (
7672              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7673               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7674              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7675               CAN_FALL(element) && WasJustFalling[x][y] &&
7676               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7677
7678              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7679               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7680               (Feld[x][y + 1] == EL_BLOCKED)))
7681     {
7682       /* this is needed for a special case not covered by calling "Impact()"
7683          from "ContinueMoving()": if an element moves to a tile directly below
7684          another element which was just falling on that tile (which was empty
7685          in the previous frame), the falling element above would just stop
7686          instead of smashing the element below (in previous version, the above
7687          element was just checked for "moving" instead of "falling", resulting
7688          in incorrect smashes caused by horizontal movement of the above
7689          element; also, the case of the player being the element to smash was
7690          simply not covered here... :-/ ) */
7691
7692       CheckCollision[x][y] = 0;
7693       CheckImpact[x][y] = 0;
7694
7695       Impact(x, y);
7696     }
7697     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7698     {
7699       if (MovDir[x][y] == MV_NONE)
7700       {
7701         InitMovingField(x, y, MV_DOWN);
7702         started_moving = TRUE;
7703       }
7704     }
7705     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7706     {
7707       if (WasJustFalling[x][y]) // prevent animation from being restarted
7708         MovDir[x][y] = MV_DOWN;
7709
7710       InitMovingField(x, y, MV_DOWN);
7711       started_moving = TRUE;
7712     }
7713     else if (element == EL_AMOEBA_DROP)
7714     {
7715       Feld[x][y] = EL_AMOEBA_GROWING;
7716       Store[x][y] = EL_AMOEBA_WET;
7717     }
7718     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7719               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7720              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7721              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7722     {
7723       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7724                                 (IS_FREE(x - 1, y + 1) ||
7725                                  Feld[x - 1][y + 1] == EL_ACID));
7726       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7727                                 (IS_FREE(x + 1, y + 1) ||
7728                                  Feld[x + 1][y + 1] == EL_ACID));
7729       boolean can_fall_any  = (can_fall_left || can_fall_right);
7730       boolean can_fall_both = (can_fall_left && can_fall_right);
7731       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7732
7733       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7734       {
7735         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7736           can_fall_right = FALSE;
7737         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7738           can_fall_left = FALSE;
7739         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7740           can_fall_right = FALSE;
7741         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7742           can_fall_left = FALSE;
7743
7744         can_fall_any  = (can_fall_left || can_fall_right);
7745         can_fall_both = FALSE;
7746       }
7747
7748       if (can_fall_both)
7749       {
7750         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7751           can_fall_right = FALSE;       // slip down on left side
7752         else
7753           can_fall_left = !(can_fall_right = RND(2));
7754
7755         can_fall_both = FALSE;
7756       }
7757
7758       if (can_fall_any)
7759       {
7760         // if not determined otherwise, prefer left side for slipping down
7761         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7762         started_moving = TRUE;
7763       }
7764     }
7765     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7766     {
7767       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7768       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7769       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7770       int belt_dir = game.belt_dir[belt_nr];
7771
7772       if ((belt_dir == MV_LEFT  && left_is_free) ||
7773           (belt_dir == MV_RIGHT && right_is_free))
7774       {
7775         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7776
7777         InitMovingField(x, y, belt_dir);
7778         started_moving = TRUE;
7779
7780         Pushed[x][y] = TRUE;
7781         Pushed[nextx][y] = TRUE;
7782
7783         GfxAction[x][y] = ACTION_DEFAULT;
7784       }
7785       else
7786       {
7787         MovDir[x][y] = 0;       // if element was moving, stop it
7788       }
7789     }
7790   }
7791
7792   // not "else if" because of elements that can fall and move (EL_SPRING)
7793   if (CAN_MOVE(element) && !started_moving)
7794   {
7795     int move_pattern = element_info[element].move_pattern;
7796     int newx, newy;
7797
7798     Moving2Blocked(x, y, &newx, &newy);
7799
7800     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7801       return;
7802
7803     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7804         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7805     {
7806       WasJustMoving[x][y] = 0;
7807       CheckCollision[x][y] = 0;
7808
7809       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7810
7811       if (Feld[x][y] != element)        // element has changed
7812         return;
7813     }
7814
7815     if (!MovDelay[x][y])        // start new movement phase
7816     {
7817       // all objects that can change their move direction after each step
7818       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7819
7820       if (element != EL_YAMYAM &&
7821           element != EL_DARK_YAMYAM &&
7822           element != EL_PACMAN &&
7823           !(move_pattern & MV_ANY_DIRECTION) &&
7824           move_pattern != MV_TURNING_LEFT &&
7825           move_pattern != MV_TURNING_RIGHT &&
7826           move_pattern != MV_TURNING_LEFT_RIGHT &&
7827           move_pattern != MV_TURNING_RIGHT_LEFT &&
7828           move_pattern != MV_TURNING_RANDOM)
7829       {
7830         TurnRound(x, y);
7831
7832         if (MovDelay[x][y] && (element == EL_BUG ||
7833                                element == EL_SPACESHIP ||
7834                                element == EL_SP_SNIKSNAK ||
7835                                element == EL_SP_ELECTRON ||
7836                                element == EL_MOLE))
7837           TEST_DrawLevelField(x, y);
7838       }
7839     }
7840
7841     if (MovDelay[x][y])         // wait some time before next movement
7842     {
7843       MovDelay[x][y]--;
7844
7845       if (element == EL_ROBOT ||
7846           element == EL_YAMYAM ||
7847           element == EL_DARK_YAMYAM)
7848       {
7849         DrawLevelElementAnimationIfNeeded(x, y, element);
7850         PlayLevelSoundAction(x, y, ACTION_WAITING);
7851       }
7852       else if (element == EL_SP_ELECTRON)
7853         DrawLevelElementAnimationIfNeeded(x, y, element);
7854       else if (element == EL_DRAGON)
7855       {
7856         int i;
7857         int dir = MovDir[x][y];
7858         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7859         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7860         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7861                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7862                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7863                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7864         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7865
7866         GfxAction[x][y] = ACTION_ATTACKING;
7867
7868         if (IS_PLAYER(x, y))
7869           DrawPlayerField(x, y);
7870         else
7871           TEST_DrawLevelField(x, y);
7872
7873         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7874
7875         for (i = 1; i <= 3; i++)
7876         {
7877           int xx = x + i * dx;
7878           int yy = y + i * dy;
7879           int sx = SCREENX(xx);
7880           int sy = SCREENY(yy);
7881           int flame_graphic = graphic + (i - 1);
7882
7883           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7884             break;
7885
7886           if (MovDelay[x][y])
7887           {
7888             int flamed = MovingOrBlocked2Element(xx, yy);
7889
7890             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7891               Bang(xx, yy);
7892             else
7893               RemoveMovingField(xx, yy);
7894
7895             ChangeDelay[xx][yy] = 0;
7896
7897             Feld[xx][yy] = EL_FLAMES;
7898
7899             if (IN_SCR_FIELD(sx, sy))
7900             {
7901               TEST_DrawLevelFieldCrumbled(xx, yy);
7902               DrawGraphic(sx, sy, flame_graphic, frame);
7903             }
7904           }
7905           else
7906           {
7907             if (Feld[xx][yy] == EL_FLAMES)
7908               Feld[xx][yy] = EL_EMPTY;
7909             TEST_DrawLevelField(xx, yy);
7910           }
7911         }
7912       }
7913
7914       if (MovDelay[x][y])       // element still has to wait some time
7915       {
7916         PlayLevelSoundAction(x, y, ACTION_WAITING);
7917
7918         return;
7919       }
7920     }
7921
7922     // now make next step
7923
7924     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7925
7926     if (DONT_COLLIDE_WITH(element) &&
7927         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7928         !PLAYER_ENEMY_PROTECTED(newx, newy))
7929     {
7930       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7931
7932       return;
7933     }
7934
7935     else if (CAN_MOVE_INTO_ACID(element) &&
7936              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7937              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7938              (MovDir[x][y] == MV_DOWN ||
7939               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7940     {
7941       SplashAcid(newx, newy);
7942       Store[x][y] = EL_ACID;
7943     }
7944     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7945     {
7946       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7947           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7948           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7949           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7950       {
7951         RemoveField(x, y);
7952         TEST_DrawLevelField(x, y);
7953
7954         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7955         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7956           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7957
7958         game.friends_still_needed--;
7959         if (!game.friends_still_needed &&
7960             !game.GameOver &&
7961             game.all_players_gone)
7962           LevelSolved();
7963
7964         return;
7965       }
7966       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7967       {
7968         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7969           TEST_DrawLevelField(newx, newy);
7970         else
7971           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7972       }
7973       else if (!IS_FREE(newx, newy))
7974       {
7975         GfxAction[x][y] = ACTION_WAITING;
7976
7977         if (IS_PLAYER(x, y))
7978           DrawPlayerField(x, y);
7979         else
7980           TEST_DrawLevelField(x, y);
7981
7982         return;
7983       }
7984     }
7985     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7986     {
7987       if (IS_FOOD_PIG(Feld[newx][newy]))
7988       {
7989         if (IS_MOVING(newx, newy))
7990           RemoveMovingField(newx, newy);
7991         else
7992         {
7993           Feld[newx][newy] = EL_EMPTY;
7994           TEST_DrawLevelField(newx, newy);
7995         }
7996
7997         PlayLevelSound(x, y, SND_PIG_DIGGING);
7998       }
7999       else if (!IS_FREE(newx, newy))
8000       {
8001         if (IS_PLAYER(x, y))
8002           DrawPlayerField(x, y);
8003         else
8004           TEST_DrawLevelField(x, y);
8005
8006         return;
8007       }
8008     }
8009     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8010     {
8011       if (Store[x][y] != EL_EMPTY)
8012       {
8013         boolean can_clone = FALSE;
8014         int xx, yy;
8015
8016         // check if element to clone is still there
8017         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8018         {
8019           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8020           {
8021             can_clone = TRUE;
8022
8023             break;
8024           }
8025         }
8026
8027         // cannot clone or target field not free anymore -- do not clone
8028         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8029           Store[x][y] = EL_EMPTY;
8030       }
8031
8032       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8033       {
8034         if (IS_MV_DIAGONAL(MovDir[x][y]))
8035         {
8036           int diagonal_move_dir = MovDir[x][y];
8037           int stored = Store[x][y];
8038           int change_delay = 8;
8039           int graphic;
8040
8041           // android is moving diagonally
8042
8043           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8044
8045           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8046           GfxElement[x][y] = EL_EMC_ANDROID;
8047           GfxAction[x][y] = ACTION_SHRINKING;
8048           GfxDir[x][y] = diagonal_move_dir;
8049           ChangeDelay[x][y] = change_delay;
8050
8051           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8052                                    GfxDir[x][y]);
8053
8054           DrawLevelGraphicAnimation(x, y, graphic);
8055           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8056
8057           if (Feld[newx][newy] == EL_ACID)
8058           {
8059             SplashAcid(newx, newy);
8060
8061             return;
8062           }
8063
8064           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8065
8066           Store[newx][newy] = EL_EMC_ANDROID;
8067           GfxElement[newx][newy] = EL_EMC_ANDROID;
8068           GfxAction[newx][newy] = ACTION_GROWING;
8069           GfxDir[newx][newy] = diagonal_move_dir;
8070           ChangeDelay[newx][newy] = change_delay;
8071
8072           graphic = el_act_dir2img(GfxElement[newx][newy],
8073                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8074
8075           DrawLevelGraphicAnimation(newx, newy, graphic);
8076           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8077
8078           return;
8079         }
8080         else
8081         {
8082           Feld[newx][newy] = EL_EMPTY;
8083           TEST_DrawLevelField(newx, newy);
8084
8085           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8086         }
8087       }
8088       else if (!IS_FREE(newx, newy))
8089       {
8090         return;
8091       }
8092     }
8093     else if (IS_CUSTOM_ELEMENT(element) &&
8094              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8095     {
8096       if (!DigFieldByCE(newx, newy, element))
8097         return;
8098
8099       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8100       {
8101         RunnerVisit[x][y] = FrameCounter;
8102         PlayerVisit[x][y] /= 8;         // expire player visit path
8103       }
8104     }
8105     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8106     {
8107       if (!IS_FREE(newx, newy))
8108       {
8109         if (IS_PLAYER(x, y))
8110           DrawPlayerField(x, y);
8111         else
8112           TEST_DrawLevelField(x, y);
8113
8114         return;
8115       }
8116       else
8117       {
8118         boolean wanna_flame = !RND(10);
8119         int dx = newx - x, dy = newy - y;
8120         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8121         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8122         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8123                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8124         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8125                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8126
8127         if ((wanna_flame ||
8128              IS_CLASSIC_ENEMY(element1) ||
8129              IS_CLASSIC_ENEMY(element2)) &&
8130             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8131             element1 != EL_FLAMES && element2 != EL_FLAMES)
8132         {
8133           ResetGfxAnimation(x, y);
8134           GfxAction[x][y] = ACTION_ATTACKING;
8135
8136           if (IS_PLAYER(x, y))
8137             DrawPlayerField(x, y);
8138           else
8139             TEST_DrawLevelField(x, y);
8140
8141           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8142
8143           MovDelay[x][y] = 50;
8144
8145           Feld[newx][newy] = EL_FLAMES;
8146           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8147             Feld[newx1][newy1] = EL_FLAMES;
8148           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8149             Feld[newx2][newy2] = EL_FLAMES;
8150
8151           return;
8152         }
8153       }
8154     }
8155     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8156              Feld[newx][newy] == EL_DIAMOND)
8157     {
8158       if (IS_MOVING(newx, newy))
8159         RemoveMovingField(newx, newy);
8160       else
8161       {
8162         Feld[newx][newy] = EL_EMPTY;
8163         TEST_DrawLevelField(newx, newy);
8164       }
8165
8166       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8167     }
8168     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8169              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8170     {
8171       if (AmoebaNr[newx][newy])
8172       {
8173         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8174         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8175             Feld[newx][newy] == EL_BD_AMOEBA)
8176           AmoebaCnt[AmoebaNr[newx][newy]]--;
8177       }
8178
8179       if (IS_MOVING(newx, newy))
8180       {
8181         RemoveMovingField(newx, newy);
8182       }
8183       else
8184       {
8185         Feld[newx][newy] = EL_EMPTY;
8186         TEST_DrawLevelField(newx, newy);
8187       }
8188
8189       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8190     }
8191     else if ((element == EL_PACMAN || element == EL_MOLE)
8192              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8193     {
8194       if (AmoebaNr[newx][newy])
8195       {
8196         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8197         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8198             Feld[newx][newy] == EL_BD_AMOEBA)
8199           AmoebaCnt[AmoebaNr[newx][newy]]--;
8200       }
8201
8202       if (element == EL_MOLE)
8203       {
8204         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8205         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8206
8207         ResetGfxAnimation(x, y);
8208         GfxAction[x][y] = ACTION_DIGGING;
8209         TEST_DrawLevelField(x, y);
8210
8211         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8212
8213         return;                         // wait for shrinking amoeba
8214       }
8215       else      // element == EL_PACMAN
8216       {
8217         Feld[newx][newy] = EL_EMPTY;
8218         TEST_DrawLevelField(newx, newy);
8219         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8220       }
8221     }
8222     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8223              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8224               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8225     {
8226       // wait for shrinking amoeba to completely disappear
8227       return;
8228     }
8229     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8230     {
8231       // object was running against a wall
8232
8233       TurnRound(x, y);
8234
8235       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8236         DrawLevelElementAnimation(x, y, element);
8237
8238       if (DONT_TOUCH(element))
8239         TestIfBadThingTouchesPlayer(x, y);
8240
8241       return;
8242     }
8243
8244     InitMovingField(x, y, MovDir[x][y]);
8245
8246     PlayLevelSoundAction(x, y, ACTION_MOVING);
8247   }
8248
8249   if (MovDir[x][y])
8250     ContinueMoving(x, y);
8251 }
8252
8253 void ContinueMoving(int x, int y)
8254 {
8255   int element = Feld[x][y];
8256   struct ElementInfo *ei = &element_info[element];
8257   int direction = MovDir[x][y];
8258   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8259   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8260   int newx = x + dx, newy = y + dy;
8261   int stored = Store[x][y];
8262   int stored_new = Store[newx][newy];
8263   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8264   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8265   boolean last_line = (newy == lev_fieldy - 1);
8266
8267   MovPos[x][y] += getElementMoveStepsize(x, y);
8268
8269   if (pushed_by_player) // special case: moving object pushed by player
8270     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8271
8272   if (ABS(MovPos[x][y]) < TILEX)
8273   {
8274     TEST_DrawLevelField(x, y);
8275
8276     return;     // element is still moving
8277   }
8278
8279   // element reached destination field
8280
8281   Feld[x][y] = EL_EMPTY;
8282   Feld[newx][newy] = element;
8283   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8284
8285   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8286   {
8287     element = Feld[newx][newy] = EL_ACID;
8288   }
8289   else if (element == EL_MOLE)
8290   {
8291     Feld[x][y] = EL_SAND;
8292
8293     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8294   }
8295   else if (element == EL_QUICKSAND_FILLING)
8296   {
8297     element = Feld[newx][newy] = get_next_element(element);
8298     Store[newx][newy] = Store[x][y];
8299   }
8300   else if (element == EL_QUICKSAND_EMPTYING)
8301   {
8302     Feld[x][y] = get_next_element(element);
8303     element = Feld[newx][newy] = Store[x][y];
8304   }
8305   else if (element == EL_QUICKSAND_FAST_FILLING)
8306   {
8307     element = Feld[newx][newy] = get_next_element(element);
8308     Store[newx][newy] = Store[x][y];
8309   }
8310   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8311   {
8312     Feld[x][y] = get_next_element(element);
8313     element = Feld[newx][newy] = Store[x][y];
8314   }
8315   else if (element == EL_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_MAGIC_WALL_DEAD;
8320     Store[newx][newy] = Store[x][y];
8321   }
8322   else if (element == EL_MAGIC_WALL_EMPTYING)
8323   {
8324     Feld[x][y] = get_next_element(element);
8325     if (!game.magic_wall_active)
8326       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8327     element = Feld[newx][newy] = Store[x][y];
8328
8329     InitField(newx, newy, FALSE);
8330   }
8331   else if (element == EL_BD_MAGIC_WALL_FILLING)
8332   {
8333     element = Feld[newx][newy] = get_next_element(element);
8334     if (!game.magic_wall_active)
8335       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8336     Store[newx][newy] = Store[x][y];
8337   }
8338   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8339   {
8340     Feld[x][y] = get_next_element(element);
8341     if (!game.magic_wall_active)
8342       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8343     element = Feld[newx][newy] = Store[x][y];
8344
8345     InitField(newx, newy, FALSE);
8346   }
8347   else if (element == EL_DC_MAGIC_WALL_FILLING)
8348   {
8349     element = Feld[newx][newy] = get_next_element(element);
8350     if (!game.magic_wall_active)
8351       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8352     Store[newx][newy] = Store[x][y];
8353   }
8354   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8355   {
8356     Feld[x][y] = get_next_element(element);
8357     if (!game.magic_wall_active)
8358       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8359     element = Feld[newx][newy] = Store[x][y];
8360
8361     InitField(newx, newy, FALSE);
8362   }
8363   else if (element == EL_AMOEBA_DROPPING)
8364   {
8365     Feld[x][y] = get_next_element(element);
8366     element = Feld[newx][newy] = Store[x][y];
8367   }
8368   else if (element == EL_SOKOBAN_OBJECT)
8369   {
8370     if (Back[x][y])
8371       Feld[x][y] = Back[x][y];
8372
8373     if (Back[newx][newy])
8374       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8375
8376     Back[x][y] = Back[newx][newy] = 0;
8377   }
8378
8379   Store[x][y] = EL_EMPTY;
8380   MovPos[x][y] = 0;
8381   MovDir[x][y] = 0;
8382   MovDelay[x][y] = 0;
8383
8384   MovDelay[newx][newy] = 0;
8385
8386   if (CAN_CHANGE_OR_HAS_ACTION(element))
8387   {
8388     // copy element change control values to new field
8389     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8390     ChangePage[newx][newy]  = ChangePage[x][y];
8391     ChangeCount[newx][newy] = ChangeCount[x][y];
8392     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8393   }
8394
8395   CustomValue[newx][newy] = CustomValue[x][y];
8396
8397   ChangeDelay[x][y] = 0;
8398   ChangePage[x][y] = -1;
8399   ChangeCount[x][y] = 0;
8400   ChangeEvent[x][y] = -1;
8401
8402   CustomValue[x][y] = 0;
8403
8404   // copy animation control values to new field
8405   GfxFrame[newx][newy]  = GfxFrame[x][y];
8406   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8407   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8408   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8409
8410   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8411
8412   // some elements can leave other elements behind after moving
8413   if (ei->move_leave_element != EL_EMPTY &&
8414       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8415       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8416   {
8417     int move_leave_element = ei->move_leave_element;
8418
8419     // this makes it possible to leave the removed element again
8420     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8421       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8422
8423     Feld[x][y] = move_leave_element;
8424
8425     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8426       MovDir[x][y] = direction;
8427
8428     InitField(x, y, FALSE);
8429
8430     if (GFX_CRUMBLED(Feld[x][y]))
8431       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8432
8433     if (ELEM_IS_PLAYER(move_leave_element))
8434       RelocatePlayer(x, y, move_leave_element);
8435   }
8436
8437   // do this after checking for left-behind element
8438   ResetGfxAnimation(x, y);      // reset animation values for old field
8439
8440   if (!CAN_MOVE(element) ||
8441       (CAN_FALL(element) && direction == MV_DOWN &&
8442        (element == EL_SPRING ||
8443         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8444         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8445     GfxDir[x][y] = MovDir[newx][newy] = 0;
8446
8447   TEST_DrawLevelField(x, y);
8448   TEST_DrawLevelField(newx, newy);
8449
8450   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8451
8452   // prevent pushed element from moving on in pushed direction
8453   if (pushed_by_player && CAN_MOVE(element) &&
8454       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8455       !(element_info[element].move_pattern & direction))
8456     TurnRound(newx, newy);
8457
8458   // prevent elements on conveyor belt from moving on in last direction
8459   if (pushed_by_conveyor && CAN_FALL(element) &&
8460       direction & MV_HORIZONTAL)
8461     MovDir[newx][newy] = 0;
8462
8463   if (!pushed_by_player)
8464   {
8465     int nextx = newx + dx, nexty = newy + dy;
8466     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8467
8468     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8469
8470     if (CAN_FALL(element) && direction == MV_DOWN)
8471       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8472
8473     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8474       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8475
8476     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8477       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8478   }
8479
8480   if (DONT_TOUCH(element))      // object may be nasty to player or others
8481   {
8482     TestIfBadThingTouchesPlayer(newx, newy);
8483     TestIfBadThingTouchesFriend(newx, newy);
8484
8485     if (!IS_CUSTOM_ELEMENT(element))
8486       TestIfBadThingTouchesOtherBadThing(newx, newy);
8487   }
8488   else if (element == EL_PENGUIN)
8489     TestIfFriendTouchesBadThing(newx, newy);
8490
8491   if (DONT_GET_HIT_BY(element))
8492   {
8493     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8494   }
8495
8496   // give the player one last chance (one more frame) to move away
8497   if (CAN_FALL(element) && direction == MV_DOWN &&
8498       (last_line || (!IS_FREE(x, newy + 1) &&
8499                      (!IS_PLAYER(x, newy + 1) ||
8500                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8501     Impact(x, newy);
8502
8503   if (pushed_by_player && !game.use_change_when_pushing_bug)
8504   {
8505     int push_side = MV_DIR_OPPOSITE(direction);
8506     struct PlayerInfo *player = PLAYERINFO(x, y);
8507
8508     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8509                                player->index_bit, push_side);
8510     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8511                                         player->index_bit, push_side);
8512   }
8513
8514   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8515     MovDelay[newx][newy] = 1;
8516
8517   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8518
8519   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8520   TestIfElementHitsCustomElement(newx, newy, direction);
8521   TestIfPlayerTouchesCustomElement(newx, newy);
8522   TestIfElementTouchesCustomElement(newx, newy);
8523
8524   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8525       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8526     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8527                              MV_DIR_OPPOSITE(direction));
8528 }
8529
8530 int AmoebeNachbarNr(int ax, int ay)
8531 {
8532   int i;
8533   int element = Feld[ax][ay];
8534   int group_nr = 0;
8535   static int xy[4][2] =
8536   {
8537     { 0, -1 },
8538     { -1, 0 },
8539     { +1, 0 },
8540     { 0, +1 }
8541   };
8542
8543   for (i = 0; i < NUM_DIRECTIONS; i++)
8544   {
8545     int x = ax + xy[i][0];
8546     int y = ay + xy[i][1];
8547
8548     if (!IN_LEV_FIELD(x, y))
8549       continue;
8550
8551     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8552       group_nr = AmoebaNr[x][y];
8553   }
8554
8555   return group_nr;
8556 }
8557
8558 static void AmoebenVereinigen(int ax, int ay)
8559 {
8560   int i, x, y, xx, yy;
8561   int new_group_nr = AmoebaNr[ax][ay];
8562   static int xy[4][2] =
8563   {
8564     { 0, -1 },
8565     { -1, 0 },
8566     { +1, 0 },
8567     { 0, +1 }
8568   };
8569
8570   if (new_group_nr == 0)
8571     return;
8572
8573   for (i = 0; i < NUM_DIRECTIONS; i++)
8574   {
8575     x = ax + xy[i][0];
8576     y = ay + xy[i][1];
8577
8578     if (!IN_LEV_FIELD(x, y))
8579       continue;
8580
8581     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8582          Feld[x][y] == EL_BD_AMOEBA ||
8583          Feld[x][y] == EL_AMOEBA_DEAD) &&
8584         AmoebaNr[x][y] != new_group_nr)
8585     {
8586       int old_group_nr = AmoebaNr[x][y];
8587
8588       if (old_group_nr == 0)
8589         return;
8590
8591       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8592       AmoebaCnt[old_group_nr] = 0;
8593       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8594       AmoebaCnt2[old_group_nr] = 0;
8595
8596       SCAN_PLAYFIELD(xx, yy)
8597       {
8598         if (AmoebaNr[xx][yy] == old_group_nr)
8599           AmoebaNr[xx][yy] = new_group_nr;
8600       }
8601     }
8602   }
8603 }
8604
8605 void AmoebeUmwandeln(int ax, int ay)
8606 {
8607   int i, x, y;
8608
8609   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8610   {
8611     int group_nr = AmoebaNr[ax][ay];
8612
8613 #ifdef DEBUG
8614     if (group_nr == 0)
8615     {
8616       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8617       printf("AmoebeUmwandeln(): This should never happen!\n");
8618       return;
8619     }
8620 #endif
8621
8622     SCAN_PLAYFIELD(x, y)
8623     {
8624       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8625       {
8626         AmoebaNr[x][y] = 0;
8627         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8628       }
8629     }
8630
8631     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8632                             SND_AMOEBA_TURNING_TO_GEM :
8633                             SND_AMOEBA_TURNING_TO_ROCK));
8634     Bang(ax, ay);
8635   }
8636   else
8637   {
8638     static int xy[4][2] =
8639     {
8640       { 0, -1 },
8641       { -1, 0 },
8642       { +1, 0 },
8643       { 0, +1 }
8644     };
8645
8646     for (i = 0; i < NUM_DIRECTIONS; i++)
8647     {
8648       x = ax + xy[i][0];
8649       y = ay + xy[i][1];
8650
8651       if (!IN_LEV_FIELD(x, y))
8652         continue;
8653
8654       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8655       {
8656         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8657                               SND_AMOEBA_TURNING_TO_GEM :
8658                               SND_AMOEBA_TURNING_TO_ROCK));
8659         Bang(x, y);
8660       }
8661     }
8662   }
8663 }
8664
8665 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8666 {
8667   int x, y;
8668   int group_nr = AmoebaNr[ax][ay];
8669   boolean done = FALSE;
8670
8671 #ifdef DEBUG
8672   if (group_nr == 0)
8673   {
8674     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8675     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8676     return;
8677   }
8678 #endif
8679
8680   SCAN_PLAYFIELD(x, y)
8681   {
8682     if (AmoebaNr[x][y] == group_nr &&
8683         (Feld[x][y] == EL_AMOEBA_DEAD ||
8684          Feld[x][y] == EL_BD_AMOEBA ||
8685          Feld[x][y] == EL_AMOEBA_GROWING))
8686     {
8687       AmoebaNr[x][y] = 0;
8688       Feld[x][y] = new_element;
8689       InitField(x, y, FALSE);
8690       TEST_DrawLevelField(x, y);
8691       done = TRUE;
8692     }
8693   }
8694
8695   if (done)
8696     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8697                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8698                             SND_BD_AMOEBA_TURNING_TO_GEM));
8699 }
8700
8701 static void AmoebeWaechst(int x, int y)
8702 {
8703   static unsigned int sound_delay = 0;
8704   static unsigned int sound_delay_value = 0;
8705
8706   if (!MovDelay[x][y])          // start new growing cycle
8707   {
8708     MovDelay[x][y] = 7;
8709
8710     if (DelayReached(&sound_delay, sound_delay_value))
8711     {
8712       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8713       sound_delay_value = 30;
8714     }
8715   }
8716
8717   if (MovDelay[x][y])           // wait some time before growing bigger
8718   {
8719     MovDelay[x][y]--;
8720     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8721     {
8722       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8723                                            6 - MovDelay[x][y]);
8724
8725       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8726     }
8727
8728     if (!MovDelay[x][y])
8729     {
8730       Feld[x][y] = Store[x][y];
8731       Store[x][y] = 0;
8732       TEST_DrawLevelField(x, y);
8733     }
8734   }
8735 }
8736
8737 static void AmoebaDisappearing(int x, int y)
8738 {
8739   static unsigned int sound_delay = 0;
8740   static unsigned int sound_delay_value = 0;
8741
8742   if (!MovDelay[x][y])          // start new shrinking cycle
8743   {
8744     MovDelay[x][y] = 7;
8745
8746     if (DelayReached(&sound_delay, sound_delay_value))
8747       sound_delay_value = 30;
8748   }
8749
8750   if (MovDelay[x][y])           // wait some time before shrinking
8751   {
8752     MovDelay[x][y]--;
8753     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8754     {
8755       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8756                                            6 - MovDelay[x][y]);
8757
8758       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8759     }
8760
8761     if (!MovDelay[x][y])
8762     {
8763       Feld[x][y] = EL_EMPTY;
8764       TEST_DrawLevelField(x, y);
8765
8766       // don't let mole enter this field in this cycle;
8767       // (give priority to objects falling to this field from above)
8768       Stop[x][y] = TRUE;
8769     }
8770   }
8771 }
8772
8773 static void AmoebeAbleger(int ax, int ay)
8774 {
8775   int i;
8776   int element = Feld[ax][ay];
8777   int graphic = el2img(element);
8778   int newax = ax, neway = ay;
8779   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8780   static int xy[4][2] =
8781   {
8782     { 0, -1 },
8783     { -1, 0 },
8784     { +1, 0 },
8785     { 0, +1 }
8786   };
8787
8788   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8789   {
8790     Feld[ax][ay] = EL_AMOEBA_DEAD;
8791     TEST_DrawLevelField(ax, ay);
8792     return;
8793   }
8794
8795   if (IS_ANIMATED(graphic))
8796     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8797
8798   if (!MovDelay[ax][ay])        // start making new amoeba field
8799     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8800
8801   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8802   {
8803     MovDelay[ax][ay]--;
8804     if (MovDelay[ax][ay])
8805       return;
8806   }
8807
8808   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8809   {
8810     int start = RND(4);
8811     int x = ax + xy[start][0];
8812     int y = ay + xy[start][1];
8813
8814     if (!IN_LEV_FIELD(x, y))
8815       return;
8816
8817     if (IS_FREE(x, y) ||
8818         CAN_GROW_INTO(Feld[x][y]) ||
8819         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8820         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8821     {
8822       newax = x;
8823       neway = y;
8824     }
8825
8826     if (newax == ax && neway == ay)
8827       return;
8828   }
8829   else                          // normal or "filled" (BD style) amoeba
8830   {
8831     int start = RND(4);
8832     boolean waiting_for_player = FALSE;
8833
8834     for (i = 0; i < NUM_DIRECTIONS; i++)
8835     {
8836       int j = (start + i) % 4;
8837       int x = ax + xy[j][0];
8838       int y = ay + xy[j][1];
8839
8840       if (!IN_LEV_FIELD(x, y))
8841         continue;
8842
8843       if (IS_FREE(x, y) ||
8844           CAN_GROW_INTO(Feld[x][y]) ||
8845           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8846           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8847       {
8848         newax = x;
8849         neway = y;
8850         break;
8851       }
8852       else if (IS_PLAYER(x, y))
8853         waiting_for_player = TRUE;
8854     }
8855
8856     if (newax == ax && neway == ay)             // amoeba cannot grow
8857     {
8858       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8859       {
8860         Feld[ax][ay] = EL_AMOEBA_DEAD;
8861         TEST_DrawLevelField(ax, ay);
8862         AmoebaCnt[AmoebaNr[ax][ay]]--;
8863
8864         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8865         {
8866           if (element == EL_AMOEBA_FULL)
8867             AmoebeUmwandeln(ax, ay);
8868           else if (element == EL_BD_AMOEBA)
8869             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8870         }
8871       }
8872       return;
8873     }
8874     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8875     {
8876       // amoeba gets larger by growing in some direction
8877
8878       int new_group_nr = AmoebaNr[ax][ay];
8879
8880 #ifdef DEBUG
8881   if (new_group_nr == 0)
8882   {
8883     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8884     printf("AmoebeAbleger(): This should never happen!\n");
8885     return;
8886   }
8887 #endif
8888
8889       AmoebaNr[newax][neway] = new_group_nr;
8890       AmoebaCnt[new_group_nr]++;
8891       AmoebaCnt2[new_group_nr]++;
8892
8893       // if amoeba touches other amoeba(s) after growing, unify them
8894       AmoebenVereinigen(newax, neway);
8895
8896       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8897       {
8898         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8899         return;
8900       }
8901     }
8902   }
8903
8904   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8905       (neway == lev_fieldy - 1 && newax != ax))
8906   {
8907     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8908     Store[newax][neway] = element;
8909   }
8910   else if (neway == ay || element == EL_EMC_DRIPPER)
8911   {
8912     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8913
8914     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8915   }
8916   else
8917   {
8918     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8919     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8920     Store[ax][ay] = EL_AMOEBA_DROP;
8921     ContinueMoving(ax, ay);
8922     return;
8923   }
8924
8925   TEST_DrawLevelField(newax, neway);
8926 }
8927
8928 static void Life(int ax, int ay)
8929 {
8930   int x1, y1, x2, y2;
8931   int life_time = 40;
8932   int element = Feld[ax][ay];
8933   int graphic = el2img(element);
8934   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8935                          level.biomaze);
8936   boolean changed = FALSE;
8937
8938   if (IS_ANIMATED(graphic))
8939     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8940
8941   if (Stop[ax][ay])
8942     return;
8943
8944   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8945     MovDelay[ax][ay] = life_time;
8946
8947   if (MovDelay[ax][ay])         // wait some time before next cycle
8948   {
8949     MovDelay[ax][ay]--;
8950     if (MovDelay[ax][ay])
8951       return;
8952   }
8953
8954   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8955   {
8956     int xx = ax+x1, yy = ay+y1;
8957     int old_element = Feld[xx][yy];
8958     int num_neighbours = 0;
8959
8960     if (!IN_LEV_FIELD(xx, yy))
8961       continue;
8962
8963     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8964     {
8965       int x = xx+x2, y = yy+y2;
8966
8967       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8968         continue;
8969
8970       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8971       boolean is_neighbour = FALSE;
8972
8973       if (level.use_life_bugs)
8974         is_neighbour =
8975           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8976            (IS_FREE(x, y)                             &&  Stop[x][y]));
8977       else
8978         is_neighbour =
8979           (Last[x][y] == element || is_player_cell);
8980
8981       if (is_neighbour)
8982         num_neighbours++;
8983     }
8984
8985     boolean is_free = FALSE;
8986
8987     if (level.use_life_bugs)
8988       is_free = (IS_FREE(xx, yy));
8989     else
8990       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8991
8992     if (xx == ax && yy == ay)           // field in the middle
8993     {
8994       if (num_neighbours < life_parameter[0] ||
8995           num_neighbours > life_parameter[1])
8996       {
8997         Feld[xx][yy] = EL_EMPTY;
8998         if (Feld[xx][yy] != old_element)
8999           TEST_DrawLevelField(xx, yy);
9000         Stop[xx][yy] = TRUE;
9001         changed = TRUE;
9002       }
9003     }
9004     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9005     {                                   // free border field
9006       if (num_neighbours >= life_parameter[2] &&
9007           num_neighbours <= life_parameter[3])
9008       {
9009         Feld[xx][yy] = element;
9010         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9011         if (Feld[xx][yy] != old_element)
9012           TEST_DrawLevelField(xx, yy);
9013         Stop[xx][yy] = TRUE;
9014         changed = TRUE;
9015       }
9016     }
9017   }
9018
9019   if (changed)
9020     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9021                    SND_GAME_OF_LIFE_GROWING);
9022 }
9023
9024 static void InitRobotWheel(int x, int y)
9025 {
9026   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9027 }
9028
9029 static void RunRobotWheel(int x, int y)
9030 {
9031   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9032 }
9033
9034 static void StopRobotWheel(int x, int y)
9035 {
9036   if (ZX == x && ZY == y)
9037   {
9038     ZX = ZY = -1;
9039
9040     game.robot_wheel_active = FALSE;
9041   }
9042 }
9043
9044 static void InitTimegateWheel(int x, int y)
9045 {
9046   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9047 }
9048
9049 static void RunTimegateWheel(int x, int y)
9050 {
9051   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9052 }
9053
9054 static void InitMagicBallDelay(int x, int y)
9055 {
9056   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9057 }
9058
9059 static void ActivateMagicBall(int bx, int by)
9060 {
9061   int x, y;
9062
9063   if (level.ball_random)
9064   {
9065     int pos_border = RND(8);    // select one of the eight border elements
9066     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9067     int xx = pos_content % 3;
9068     int yy = pos_content / 3;
9069
9070     x = bx - 1 + xx;
9071     y = by - 1 + yy;
9072
9073     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9074       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9075   }
9076   else
9077   {
9078     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9079     {
9080       int xx = x - bx + 1;
9081       int yy = y - by + 1;
9082
9083       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9084         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9085     }
9086   }
9087
9088   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9089 }
9090
9091 static void CheckExit(int x, int y)
9092 {
9093   if (game.gems_still_needed > 0 ||
9094       game.sokoban_fields_still_needed > 0 ||
9095       game.sokoban_objects_still_needed > 0 ||
9096       game.lights_still_needed > 0)
9097   {
9098     int element = Feld[x][y];
9099     int graphic = el2img(element);
9100
9101     if (IS_ANIMATED(graphic))
9102       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9103
9104     return;
9105   }
9106
9107   // do not re-open exit door closed after last player
9108   if (game.all_players_gone)
9109     return;
9110
9111   Feld[x][y] = EL_EXIT_OPENING;
9112
9113   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9114 }
9115
9116 static void CheckExitEM(int x, int y)
9117 {
9118   if (game.gems_still_needed > 0 ||
9119       game.sokoban_fields_still_needed > 0 ||
9120       game.sokoban_objects_still_needed > 0 ||
9121       game.lights_still_needed > 0)
9122   {
9123     int element = Feld[x][y];
9124     int graphic = el2img(element);
9125
9126     if (IS_ANIMATED(graphic))
9127       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9128
9129     return;
9130   }
9131
9132   // do not re-open exit door closed after last player
9133   if (game.all_players_gone)
9134     return;
9135
9136   Feld[x][y] = EL_EM_EXIT_OPENING;
9137
9138   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9139 }
9140
9141 static void CheckExitSteel(int x, int y)
9142 {
9143   if (game.gems_still_needed > 0 ||
9144       game.sokoban_fields_still_needed > 0 ||
9145       game.sokoban_objects_still_needed > 0 ||
9146       game.lights_still_needed > 0)
9147   {
9148     int element = Feld[x][y];
9149     int graphic = el2img(element);
9150
9151     if (IS_ANIMATED(graphic))
9152       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9153
9154     return;
9155   }
9156
9157   // do not re-open exit door closed after last player
9158   if (game.all_players_gone)
9159     return;
9160
9161   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9162
9163   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9164 }
9165
9166 static void CheckExitSteelEM(int x, int y)
9167 {
9168   if (game.gems_still_needed > 0 ||
9169       game.sokoban_fields_still_needed > 0 ||
9170       game.sokoban_objects_still_needed > 0 ||
9171       game.lights_still_needed > 0)
9172   {
9173     int element = Feld[x][y];
9174     int graphic = el2img(element);
9175
9176     if (IS_ANIMATED(graphic))
9177       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9178
9179     return;
9180   }
9181
9182   // do not re-open exit door closed after last player
9183   if (game.all_players_gone)
9184     return;
9185
9186   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9187
9188   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9189 }
9190
9191 static void CheckExitSP(int x, int y)
9192 {
9193   if (game.gems_still_needed > 0)
9194   {
9195     int element = Feld[x][y];
9196     int graphic = el2img(element);
9197
9198     if (IS_ANIMATED(graphic))
9199       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9200
9201     return;
9202   }
9203
9204   // do not re-open exit door closed after last player
9205   if (game.all_players_gone)
9206     return;
9207
9208   Feld[x][y] = EL_SP_EXIT_OPENING;
9209
9210   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9211 }
9212
9213 static void CloseAllOpenTimegates(void)
9214 {
9215   int x, y;
9216
9217   SCAN_PLAYFIELD(x, y)
9218   {
9219     int element = Feld[x][y];
9220
9221     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9222     {
9223       Feld[x][y] = EL_TIMEGATE_CLOSING;
9224
9225       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9226     }
9227   }
9228 }
9229
9230 static void DrawTwinkleOnField(int x, int y)
9231 {
9232   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9233     return;
9234
9235   if (Feld[x][y] == EL_BD_DIAMOND)
9236     return;
9237
9238   if (MovDelay[x][y] == 0)      // next animation frame
9239     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9240
9241   if (MovDelay[x][y] != 0)      // wait some time before next frame
9242   {
9243     MovDelay[x][y]--;
9244
9245     DrawLevelElementAnimation(x, y, Feld[x][y]);
9246
9247     if (MovDelay[x][y] != 0)
9248     {
9249       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9250                                            10 - MovDelay[x][y]);
9251
9252       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9253     }
9254   }
9255 }
9256
9257 static void MauerWaechst(int x, int y)
9258 {
9259   int delay = 6;
9260
9261   if (!MovDelay[x][y])          // next animation frame
9262     MovDelay[x][y] = 3 * delay;
9263
9264   if (MovDelay[x][y])           // wait some time before next frame
9265   {
9266     MovDelay[x][y]--;
9267
9268     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9269     {
9270       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9271       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9272
9273       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9274     }
9275
9276     if (!MovDelay[x][y])
9277     {
9278       if (MovDir[x][y] == MV_LEFT)
9279       {
9280         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9281           TEST_DrawLevelField(x - 1, y);
9282       }
9283       else if (MovDir[x][y] == MV_RIGHT)
9284       {
9285         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9286           TEST_DrawLevelField(x + 1, y);
9287       }
9288       else if (MovDir[x][y] == MV_UP)
9289       {
9290         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9291           TEST_DrawLevelField(x, y - 1);
9292       }
9293       else
9294       {
9295         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9296           TEST_DrawLevelField(x, y + 1);
9297       }
9298
9299       Feld[x][y] = Store[x][y];
9300       Store[x][y] = 0;
9301       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9302       TEST_DrawLevelField(x, y);
9303     }
9304   }
9305 }
9306
9307 static void MauerAbleger(int ax, int ay)
9308 {
9309   int element = Feld[ax][ay];
9310   int graphic = el2img(element);
9311   boolean oben_frei = FALSE, unten_frei = FALSE;
9312   boolean links_frei = FALSE, rechts_frei = FALSE;
9313   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9314   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9315   boolean new_wall = FALSE;
9316
9317   if (IS_ANIMATED(graphic))
9318     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9319
9320   if (!MovDelay[ax][ay])        // start building new wall
9321     MovDelay[ax][ay] = 6;
9322
9323   if (MovDelay[ax][ay])         // wait some time before building new wall
9324   {
9325     MovDelay[ax][ay]--;
9326     if (MovDelay[ax][ay])
9327       return;
9328   }
9329
9330   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9331     oben_frei = TRUE;
9332   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9333     unten_frei = TRUE;
9334   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9335     links_frei = TRUE;
9336   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9337     rechts_frei = TRUE;
9338
9339   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9340       element == EL_EXPANDABLE_WALL_ANY)
9341   {
9342     if (oben_frei)
9343     {
9344       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9345       Store[ax][ay-1] = element;
9346       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9347       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9348         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9349                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9350       new_wall = TRUE;
9351     }
9352     if (unten_frei)
9353     {
9354       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9355       Store[ax][ay+1] = element;
9356       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9357       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9358         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9359                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9360       new_wall = TRUE;
9361     }
9362   }
9363
9364   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9365       element == EL_EXPANDABLE_WALL_ANY ||
9366       element == EL_EXPANDABLE_WALL ||
9367       element == EL_BD_EXPANDABLE_WALL)
9368   {
9369     if (links_frei)
9370     {
9371       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9372       Store[ax-1][ay] = element;
9373       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9374       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9375         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9376                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9377       new_wall = TRUE;
9378     }
9379
9380     if (rechts_frei)
9381     {
9382       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9383       Store[ax+1][ay] = element;
9384       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9385       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9386         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9387                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9388       new_wall = TRUE;
9389     }
9390   }
9391
9392   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9393     TEST_DrawLevelField(ax, ay);
9394
9395   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9396     oben_massiv = TRUE;
9397   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9398     unten_massiv = TRUE;
9399   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9400     links_massiv = TRUE;
9401   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9402     rechts_massiv = TRUE;
9403
9404   if (((oben_massiv && unten_massiv) ||
9405        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9406        element == EL_EXPANDABLE_WALL) &&
9407       ((links_massiv && rechts_massiv) ||
9408        element == EL_EXPANDABLE_WALL_VERTICAL))
9409     Feld[ax][ay] = EL_WALL;
9410
9411   if (new_wall)
9412     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9413 }
9414
9415 static void MauerAblegerStahl(int ax, int ay)
9416 {
9417   int element = Feld[ax][ay];
9418   int graphic = el2img(element);
9419   boolean oben_frei = FALSE, unten_frei = FALSE;
9420   boolean links_frei = FALSE, rechts_frei = FALSE;
9421   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9422   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9423   boolean new_wall = FALSE;
9424
9425   if (IS_ANIMATED(graphic))
9426     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9427
9428   if (!MovDelay[ax][ay])        // start building new wall
9429     MovDelay[ax][ay] = 6;
9430
9431   if (MovDelay[ax][ay])         // wait some time before building new wall
9432   {
9433     MovDelay[ax][ay]--;
9434     if (MovDelay[ax][ay])
9435       return;
9436   }
9437
9438   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9439     oben_frei = TRUE;
9440   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9441     unten_frei = TRUE;
9442   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9443     links_frei = TRUE;
9444   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9445     rechts_frei = TRUE;
9446
9447   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9448       element == EL_EXPANDABLE_STEELWALL_ANY)
9449   {
9450     if (oben_frei)
9451     {
9452       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9453       Store[ax][ay-1] = element;
9454       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9455       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9456         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9457                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9458       new_wall = TRUE;
9459     }
9460     if (unten_frei)
9461     {
9462       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9463       Store[ax][ay+1] = element;
9464       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9465       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9466         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9467                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9468       new_wall = TRUE;
9469     }
9470   }
9471
9472   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9473       element == EL_EXPANDABLE_STEELWALL_ANY)
9474   {
9475     if (links_frei)
9476     {
9477       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9478       Store[ax-1][ay] = element;
9479       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9480       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9481         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9482                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9483       new_wall = TRUE;
9484     }
9485
9486     if (rechts_frei)
9487     {
9488       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9489       Store[ax+1][ay] = element;
9490       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9491       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9492         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9493                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9494       new_wall = TRUE;
9495     }
9496   }
9497
9498   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9499     oben_massiv = TRUE;
9500   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9501     unten_massiv = TRUE;
9502   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9503     links_massiv = TRUE;
9504   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9505     rechts_massiv = TRUE;
9506
9507   if (((oben_massiv && unten_massiv) ||
9508        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9509       ((links_massiv && rechts_massiv) ||
9510        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9511     Feld[ax][ay] = EL_STEELWALL;
9512
9513   if (new_wall)
9514     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9515 }
9516
9517 static void CheckForDragon(int x, int y)
9518 {
9519   int i, j;
9520   boolean dragon_found = FALSE;
9521   static int xy[4][2] =
9522   {
9523     { 0, -1 },
9524     { -1, 0 },
9525     { +1, 0 },
9526     { 0, +1 }
9527   };
9528
9529   for (i = 0; i < NUM_DIRECTIONS; i++)
9530   {
9531     for (j = 0; j < 4; j++)
9532     {
9533       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9534
9535       if (IN_LEV_FIELD(xx, yy) &&
9536           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9537       {
9538         if (Feld[xx][yy] == EL_DRAGON)
9539           dragon_found = TRUE;
9540       }
9541       else
9542         break;
9543     }
9544   }
9545
9546   if (!dragon_found)
9547   {
9548     for (i = 0; i < NUM_DIRECTIONS; i++)
9549     {
9550       for (j = 0; j < 3; j++)
9551       {
9552         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9553   
9554         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9555         {
9556           Feld[xx][yy] = EL_EMPTY;
9557           TEST_DrawLevelField(xx, yy);
9558         }
9559         else
9560           break;
9561       }
9562     }
9563   }
9564 }
9565
9566 static void InitBuggyBase(int x, int y)
9567 {
9568   int element = Feld[x][y];
9569   int activating_delay = FRAMES_PER_SECOND / 4;
9570
9571   ChangeDelay[x][y] =
9572     (element == EL_SP_BUGGY_BASE ?
9573      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9574      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9575      activating_delay :
9576      element == EL_SP_BUGGY_BASE_ACTIVE ?
9577      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9578 }
9579
9580 static void WarnBuggyBase(int x, int y)
9581 {
9582   int i;
9583   static int xy[4][2] =
9584   {
9585     { 0, -1 },
9586     { -1, 0 },
9587     { +1, 0 },
9588     { 0, +1 }
9589   };
9590
9591   for (i = 0; i < NUM_DIRECTIONS; i++)
9592   {
9593     int xx = x + xy[i][0];
9594     int yy = y + xy[i][1];
9595
9596     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9597     {
9598       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9599
9600       break;
9601     }
9602   }
9603 }
9604
9605 static void InitTrap(int x, int y)
9606 {
9607   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9608 }
9609
9610 static void ActivateTrap(int x, int y)
9611 {
9612   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9613 }
9614
9615 static void ChangeActiveTrap(int x, int y)
9616 {
9617   int graphic = IMG_TRAP_ACTIVE;
9618
9619   // if new animation frame was drawn, correct crumbled sand border
9620   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9621     TEST_DrawLevelFieldCrumbled(x, y);
9622 }
9623
9624 static int getSpecialActionElement(int element, int number, int base_element)
9625 {
9626   return (element != EL_EMPTY ? element :
9627           number != -1 ? base_element + number - 1 :
9628           EL_EMPTY);
9629 }
9630
9631 static int getModifiedActionNumber(int value_old, int operator, int operand,
9632                                    int value_min, int value_max)
9633 {
9634   int value_new = (operator == CA_MODE_SET      ? operand :
9635                    operator == CA_MODE_ADD      ? value_old + operand :
9636                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9637                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9638                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9639                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9640                    value_old);
9641
9642   return (value_new < value_min ? value_min :
9643           value_new > value_max ? value_max :
9644           value_new);
9645 }
9646
9647 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9648 {
9649   struct ElementInfo *ei = &element_info[element];
9650   struct ElementChangeInfo *change = &ei->change_page[page];
9651   int target_element = change->target_element;
9652   int action_type = change->action_type;
9653   int action_mode = change->action_mode;
9654   int action_arg = change->action_arg;
9655   int action_element = change->action_element;
9656   int i;
9657
9658   if (!change->has_action)
9659     return;
9660
9661   // ---------- determine action paramater values -----------------------------
9662
9663   int level_time_value =
9664     (level.time > 0 ? TimeLeft :
9665      TimePlayed);
9666
9667   int action_arg_element_raw =
9668     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9669      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9670      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9671      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9672      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9673      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9674      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9675      EL_EMPTY);
9676   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9677
9678   int action_arg_direction =
9679     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9680      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9681      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9682      change->actual_trigger_side :
9683      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9684      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9685      MV_NONE);
9686
9687   int action_arg_number_min =
9688     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9689      CA_ARG_MIN);
9690
9691   int action_arg_number_max =
9692     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9693      action_type == CA_SET_LEVEL_GEMS ? 999 :
9694      action_type == CA_SET_LEVEL_TIME ? 9999 :
9695      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9696      action_type == CA_SET_CE_VALUE ? 9999 :
9697      action_type == CA_SET_CE_SCORE ? 9999 :
9698      CA_ARG_MAX);
9699
9700   int action_arg_number_reset =
9701     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9702      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9703      action_type == CA_SET_LEVEL_TIME ? level.time :
9704      action_type == CA_SET_LEVEL_SCORE ? 0 :
9705      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9706      action_type == CA_SET_CE_SCORE ? 0 :
9707      0);
9708
9709   int action_arg_number =
9710     (action_arg <= CA_ARG_MAX ? action_arg :
9711      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9712      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9713      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9714      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9715      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9716      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9717      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9718      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9719      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9720      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9721      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9722      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9723      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9724      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9725      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9726      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9727      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9728      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9729      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9730      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9731      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9732      -1);
9733
9734   int action_arg_number_old =
9735     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9736      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9737      action_type == CA_SET_LEVEL_SCORE ? game.score :
9738      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9739      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9740      0);
9741
9742   int action_arg_number_new =
9743     getModifiedActionNumber(action_arg_number_old,
9744                             action_mode, action_arg_number,
9745                             action_arg_number_min, action_arg_number_max);
9746
9747   int trigger_player_bits =
9748     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9749      change->actual_trigger_player_bits : change->trigger_player);
9750
9751   int action_arg_player_bits =
9752     (action_arg >= CA_ARG_PLAYER_1 &&
9753      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9754      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9755      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9756      PLAYER_BITS_ANY);
9757
9758   // ---------- execute action  -----------------------------------------------
9759
9760   switch (action_type)
9761   {
9762     case CA_NO_ACTION:
9763     {
9764       return;
9765     }
9766
9767     // ---------- level actions  ----------------------------------------------
9768
9769     case CA_RESTART_LEVEL:
9770     {
9771       game.restart_level = TRUE;
9772
9773       break;
9774     }
9775
9776     case CA_SHOW_ENVELOPE:
9777     {
9778       int element = getSpecialActionElement(action_arg_element,
9779                                             action_arg_number, EL_ENVELOPE_1);
9780
9781       if (IS_ENVELOPE(element))
9782         local_player->show_envelope = element;
9783
9784       break;
9785     }
9786
9787     case CA_SET_LEVEL_TIME:
9788     {
9789       if (level.time > 0)       // only modify limited time value
9790       {
9791         TimeLeft = action_arg_number_new;
9792
9793         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9794
9795         DisplayGameControlValues();
9796
9797         if (!TimeLeft && setup.time_limit)
9798           for (i = 0; i < MAX_PLAYERS; i++)
9799             KillPlayer(&stored_player[i]);
9800       }
9801
9802       break;
9803     }
9804
9805     case CA_SET_LEVEL_SCORE:
9806     {
9807       game.score = action_arg_number_new;
9808
9809       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9810
9811       DisplayGameControlValues();
9812
9813       break;
9814     }
9815
9816     case CA_SET_LEVEL_GEMS:
9817     {
9818       game.gems_still_needed = action_arg_number_new;
9819
9820       game.snapshot.collected_item = TRUE;
9821
9822       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9823
9824       DisplayGameControlValues();
9825
9826       break;
9827     }
9828
9829     case CA_SET_LEVEL_WIND:
9830     {
9831       game.wind_direction = action_arg_direction;
9832
9833       break;
9834     }
9835
9836     case CA_SET_LEVEL_RANDOM_SEED:
9837     {
9838       // ensure that setting a new random seed while playing is predictable
9839       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9840
9841       break;
9842     }
9843
9844     // ---------- player actions  ---------------------------------------------
9845
9846     case CA_MOVE_PLAYER:
9847     {
9848       // automatically move to the next field in specified direction
9849       for (i = 0; i < MAX_PLAYERS; i++)
9850         if (trigger_player_bits & (1 << i))
9851           stored_player[i].programmed_action = action_arg_direction;
9852
9853       break;
9854     }
9855
9856     case CA_EXIT_PLAYER:
9857     {
9858       for (i = 0; i < MAX_PLAYERS; i++)
9859         if (action_arg_player_bits & (1 << i))
9860           ExitPlayer(&stored_player[i]);
9861
9862       if (game.players_still_needed == 0)
9863         LevelSolved();
9864
9865       break;
9866     }
9867
9868     case CA_KILL_PLAYER:
9869     {
9870       for (i = 0; i < MAX_PLAYERS; i++)
9871         if (action_arg_player_bits & (1 << i))
9872           KillPlayer(&stored_player[i]);
9873
9874       break;
9875     }
9876
9877     case CA_SET_PLAYER_KEYS:
9878     {
9879       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9880       int element = getSpecialActionElement(action_arg_element,
9881                                             action_arg_number, EL_KEY_1);
9882
9883       if (IS_KEY(element))
9884       {
9885         for (i = 0; i < MAX_PLAYERS; i++)
9886         {
9887           if (trigger_player_bits & (1 << i))
9888           {
9889             stored_player[i].key[KEY_NR(element)] = key_state;
9890
9891             DrawGameDoorValues();
9892           }
9893         }
9894       }
9895
9896       break;
9897     }
9898
9899     case CA_SET_PLAYER_SPEED:
9900     {
9901       for (i = 0; i < MAX_PLAYERS; i++)
9902       {
9903         if (trigger_player_bits & (1 << i))
9904         {
9905           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9906
9907           if (action_arg == CA_ARG_SPEED_FASTER &&
9908               stored_player[i].cannot_move)
9909           {
9910             action_arg_number = STEPSIZE_VERY_SLOW;
9911           }
9912           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9913                    action_arg == CA_ARG_SPEED_FASTER)
9914           {
9915             action_arg_number = 2;
9916             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9917                            CA_MODE_MULTIPLY);
9918           }
9919           else if (action_arg == CA_ARG_NUMBER_RESET)
9920           {
9921             action_arg_number = level.initial_player_stepsize[i];
9922           }
9923
9924           move_stepsize =
9925             getModifiedActionNumber(move_stepsize,
9926                                     action_mode,
9927                                     action_arg_number,
9928                                     action_arg_number_min,
9929                                     action_arg_number_max);
9930
9931           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9932         }
9933       }
9934
9935       break;
9936     }
9937
9938     case CA_SET_PLAYER_SHIELD:
9939     {
9940       for (i = 0; i < MAX_PLAYERS; i++)
9941       {
9942         if (trigger_player_bits & (1 << i))
9943         {
9944           if (action_arg == CA_ARG_SHIELD_OFF)
9945           {
9946             stored_player[i].shield_normal_time_left = 0;
9947             stored_player[i].shield_deadly_time_left = 0;
9948           }
9949           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9950           {
9951             stored_player[i].shield_normal_time_left = 999999;
9952           }
9953           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9954           {
9955             stored_player[i].shield_normal_time_left = 999999;
9956             stored_player[i].shield_deadly_time_left = 999999;
9957           }
9958         }
9959       }
9960
9961       break;
9962     }
9963
9964     case CA_SET_PLAYER_GRAVITY:
9965     {
9966       for (i = 0; i < MAX_PLAYERS; i++)
9967       {
9968         if (trigger_player_bits & (1 << i))
9969         {
9970           stored_player[i].gravity =
9971             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9972              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9973              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9974              stored_player[i].gravity);
9975         }
9976       }
9977
9978       break;
9979     }
9980
9981     case CA_SET_PLAYER_ARTWORK:
9982     {
9983       for (i = 0; i < MAX_PLAYERS; i++)
9984       {
9985         if (trigger_player_bits & (1 << i))
9986         {
9987           int artwork_element = action_arg_element;
9988
9989           if (action_arg == CA_ARG_ELEMENT_RESET)
9990             artwork_element =
9991               (level.use_artwork_element[i] ? level.artwork_element[i] :
9992                stored_player[i].element_nr);
9993
9994           if (stored_player[i].artwork_element != artwork_element)
9995             stored_player[i].Frame = 0;
9996
9997           stored_player[i].artwork_element = artwork_element;
9998
9999           SetPlayerWaiting(&stored_player[i], FALSE);
10000
10001           // set number of special actions for bored and sleeping animation
10002           stored_player[i].num_special_action_bored =
10003             get_num_special_action(artwork_element,
10004                                    ACTION_BORING_1, ACTION_BORING_LAST);
10005           stored_player[i].num_special_action_sleeping =
10006             get_num_special_action(artwork_element,
10007                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10008         }
10009       }
10010
10011       break;
10012     }
10013
10014     case CA_SET_PLAYER_INVENTORY:
10015     {
10016       for (i = 0; i < MAX_PLAYERS; i++)
10017       {
10018         struct PlayerInfo *player = &stored_player[i];
10019         int j, k;
10020
10021         if (trigger_player_bits & (1 << i))
10022         {
10023           int inventory_element = action_arg_element;
10024
10025           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10026               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10027               action_arg == CA_ARG_ELEMENT_ACTION)
10028           {
10029             int element = inventory_element;
10030             int collect_count = element_info[element].collect_count_initial;
10031
10032             if (!IS_CUSTOM_ELEMENT(element))
10033               collect_count = 1;
10034
10035             if (collect_count == 0)
10036               player->inventory_infinite_element = element;
10037             else
10038               for (k = 0; k < collect_count; k++)
10039                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10040                   player->inventory_element[player->inventory_size++] =
10041                     element;
10042           }
10043           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10044                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10045                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10046           {
10047             if (player->inventory_infinite_element != EL_UNDEFINED &&
10048                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10049                                      action_arg_element_raw))
10050               player->inventory_infinite_element = EL_UNDEFINED;
10051
10052             for (k = 0, j = 0; j < player->inventory_size; j++)
10053             {
10054               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10055                                         action_arg_element_raw))
10056                 player->inventory_element[k++] = player->inventory_element[j];
10057             }
10058
10059             player->inventory_size = k;
10060           }
10061           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10062           {
10063             if (player->inventory_size > 0)
10064             {
10065               for (j = 0; j < player->inventory_size - 1; j++)
10066                 player->inventory_element[j] = player->inventory_element[j + 1];
10067
10068               player->inventory_size--;
10069             }
10070           }
10071           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10072           {
10073             if (player->inventory_size > 0)
10074               player->inventory_size--;
10075           }
10076           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10077           {
10078             player->inventory_infinite_element = EL_UNDEFINED;
10079             player->inventory_size = 0;
10080           }
10081           else if (action_arg == CA_ARG_INVENTORY_RESET)
10082           {
10083             player->inventory_infinite_element = EL_UNDEFINED;
10084             player->inventory_size = 0;
10085
10086             if (level.use_initial_inventory[i])
10087             {
10088               for (j = 0; j < level.initial_inventory_size[i]; j++)
10089               {
10090                 int element = level.initial_inventory_content[i][j];
10091                 int collect_count = element_info[element].collect_count_initial;
10092
10093                 if (!IS_CUSTOM_ELEMENT(element))
10094                   collect_count = 1;
10095
10096                 if (collect_count == 0)
10097                   player->inventory_infinite_element = element;
10098                 else
10099                   for (k = 0; k < collect_count; k++)
10100                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10101                       player->inventory_element[player->inventory_size++] =
10102                         element;
10103               }
10104             }
10105           }
10106         }
10107       }
10108
10109       break;
10110     }
10111
10112     // ---------- CE actions  -------------------------------------------------
10113
10114     case CA_SET_CE_VALUE:
10115     {
10116       int last_ce_value = CustomValue[x][y];
10117
10118       CustomValue[x][y] = action_arg_number_new;
10119
10120       if (CustomValue[x][y] != last_ce_value)
10121       {
10122         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10123         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10124
10125         if (CustomValue[x][y] == 0)
10126         {
10127           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10128           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10129         }
10130       }
10131
10132       break;
10133     }
10134
10135     case CA_SET_CE_SCORE:
10136     {
10137       int last_ce_score = ei->collect_score;
10138
10139       ei->collect_score = action_arg_number_new;
10140
10141       if (ei->collect_score != last_ce_score)
10142       {
10143         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10144         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10145
10146         if (ei->collect_score == 0)
10147         {
10148           int xx, yy;
10149
10150           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10151           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10152
10153           /*
10154             This is a very special case that seems to be a mixture between
10155             CheckElementChange() and CheckTriggeredElementChange(): while
10156             the first one only affects single elements that are triggered
10157             directly, the second one affects multiple elements in the playfield
10158             that are triggered indirectly by another element. This is a third
10159             case: Changing the CE score always affects multiple identical CEs,
10160             so every affected CE must be checked, not only the single CE for
10161             which the CE score was changed in the first place (as every instance
10162             of that CE shares the same CE score, and therefore also can change)!
10163           */
10164           SCAN_PLAYFIELD(xx, yy)
10165           {
10166             if (Feld[xx][yy] == element)
10167               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10168                                  CE_SCORE_GETS_ZERO);
10169           }
10170         }
10171       }
10172
10173       break;
10174     }
10175
10176     case CA_SET_CE_ARTWORK:
10177     {
10178       int artwork_element = action_arg_element;
10179       boolean reset_frame = FALSE;
10180       int xx, yy;
10181
10182       if (action_arg == CA_ARG_ELEMENT_RESET)
10183         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10184                            element);
10185
10186       if (ei->gfx_element != artwork_element)
10187         reset_frame = TRUE;
10188
10189       ei->gfx_element = artwork_element;
10190
10191       SCAN_PLAYFIELD(xx, yy)
10192       {
10193         if (Feld[xx][yy] == element)
10194         {
10195           if (reset_frame)
10196           {
10197             ResetGfxAnimation(xx, yy);
10198             ResetRandomAnimationValue(xx, yy);
10199           }
10200
10201           TEST_DrawLevelField(xx, yy);
10202         }
10203       }
10204
10205       break;
10206     }
10207
10208     // ---------- engine actions  ---------------------------------------------
10209
10210     case CA_SET_ENGINE_SCAN_MODE:
10211     {
10212       InitPlayfieldScanMode(action_arg);
10213
10214       break;
10215     }
10216
10217     default:
10218       break;
10219   }
10220 }
10221
10222 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10223 {
10224   int old_element = Feld[x][y];
10225   int new_element = GetElementFromGroupElement(element);
10226   int previous_move_direction = MovDir[x][y];
10227   int last_ce_value = CustomValue[x][y];
10228   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10229   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10230   boolean add_player_onto_element = (new_element_is_player &&
10231                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10232                                      IS_WALKABLE(old_element));
10233
10234   if (!add_player_onto_element)
10235   {
10236     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10237       RemoveMovingField(x, y);
10238     else
10239       RemoveField(x, y);
10240
10241     Feld[x][y] = new_element;
10242
10243     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10244       MovDir[x][y] = previous_move_direction;
10245
10246     if (element_info[new_element].use_last_ce_value)
10247       CustomValue[x][y] = last_ce_value;
10248
10249     InitField_WithBug1(x, y, FALSE);
10250
10251     new_element = Feld[x][y];   // element may have changed
10252
10253     ResetGfxAnimation(x, y);
10254     ResetRandomAnimationValue(x, y);
10255
10256     TEST_DrawLevelField(x, y);
10257
10258     if (GFX_CRUMBLED(new_element))
10259       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10260   }
10261
10262   // check if element under the player changes from accessible to unaccessible
10263   // (needed for special case of dropping element which then changes)
10264   // (must be checked after creating new element for walkable group elements)
10265   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10266       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10267   {
10268     Bang(x, y);
10269
10270     return;
10271   }
10272
10273   // "ChangeCount" not set yet to allow "entered by player" change one time
10274   if (new_element_is_player)
10275     RelocatePlayer(x, y, new_element);
10276
10277   if (is_change)
10278     ChangeCount[x][y]++;        // count number of changes in the same frame
10279
10280   TestIfBadThingTouchesPlayer(x, y);
10281   TestIfPlayerTouchesCustomElement(x, y);
10282   TestIfElementTouchesCustomElement(x, y);
10283 }
10284
10285 static void CreateField(int x, int y, int element)
10286 {
10287   CreateFieldExt(x, y, element, FALSE);
10288 }
10289
10290 static void CreateElementFromChange(int x, int y, int element)
10291 {
10292   element = GET_VALID_RUNTIME_ELEMENT(element);
10293
10294   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10295   {
10296     int old_element = Feld[x][y];
10297
10298     // prevent changed element from moving in same engine frame
10299     // unless both old and new element can either fall or move
10300     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10301         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10302       Stop[x][y] = TRUE;
10303   }
10304
10305   CreateFieldExt(x, y, element, TRUE);
10306 }
10307
10308 static boolean ChangeElement(int x, int y, int element, int page)
10309 {
10310   struct ElementInfo *ei = &element_info[element];
10311   struct ElementChangeInfo *change = &ei->change_page[page];
10312   int ce_value = CustomValue[x][y];
10313   int ce_score = ei->collect_score;
10314   int target_element;
10315   int old_element = Feld[x][y];
10316
10317   // always use default change event to prevent running into a loop
10318   if (ChangeEvent[x][y] == -1)
10319     ChangeEvent[x][y] = CE_DELAY;
10320
10321   if (ChangeEvent[x][y] == CE_DELAY)
10322   {
10323     // reset actual trigger element, trigger player and action element
10324     change->actual_trigger_element = EL_EMPTY;
10325     change->actual_trigger_player = EL_EMPTY;
10326     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10327     change->actual_trigger_side = CH_SIDE_NONE;
10328     change->actual_trigger_ce_value = 0;
10329     change->actual_trigger_ce_score = 0;
10330   }
10331
10332   // do not change elements more than a specified maximum number of changes
10333   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10334     return FALSE;
10335
10336   ChangeCount[x][y]++;          // count number of changes in the same frame
10337
10338   if (change->explode)
10339   {
10340     Bang(x, y);
10341
10342     return TRUE;
10343   }
10344
10345   if (change->use_target_content)
10346   {
10347     boolean complete_replace = TRUE;
10348     boolean can_replace[3][3];
10349     int xx, yy;
10350
10351     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10352     {
10353       boolean is_empty;
10354       boolean is_walkable;
10355       boolean is_diggable;
10356       boolean is_collectible;
10357       boolean is_removable;
10358       boolean is_destructible;
10359       int ex = x + xx - 1;
10360       int ey = y + yy - 1;
10361       int content_element = change->target_content.e[xx][yy];
10362       int e;
10363
10364       can_replace[xx][yy] = TRUE;
10365
10366       if (ex == x && ey == y)   // do not check changing element itself
10367         continue;
10368
10369       if (content_element == EL_EMPTY_SPACE)
10370       {
10371         can_replace[xx][yy] = FALSE;    // do not replace border with space
10372
10373         continue;
10374       }
10375
10376       if (!IN_LEV_FIELD(ex, ey))
10377       {
10378         can_replace[xx][yy] = FALSE;
10379         complete_replace = FALSE;
10380
10381         continue;
10382       }
10383
10384       e = Feld[ex][ey];
10385
10386       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10387         e = MovingOrBlocked2Element(ex, ey);
10388
10389       is_empty = (IS_FREE(ex, ey) ||
10390                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10391
10392       is_walkable     = (is_empty || IS_WALKABLE(e));
10393       is_diggable     = (is_empty || IS_DIGGABLE(e));
10394       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10395       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10396       is_removable    = (is_diggable || is_collectible);
10397
10398       can_replace[xx][yy] =
10399         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10400           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10401           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10402           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10403           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10404           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10405          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10406
10407       if (!can_replace[xx][yy])
10408         complete_replace = FALSE;
10409     }
10410
10411     if (!change->only_if_complete || complete_replace)
10412     {
10413       boolean something_has_changed = FALSE;
10414
10415       if (change->only_if_complete && change->use_random_replace &&
10416           RND(100) < change->random_percentage)
10417         return FALSE;
10418
10419       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10420       {
10421         int ex = x + xx - 1;
10422         int ey = y + yy - 1;
10423         int content_element;
10424
10425         if (can_replace[xx][yy] && (!change->use_random_replace ||
10426                                     RND(100) < change->random_percentage))
10427         {
10428           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10429             RemoveMovingField(ex, ey);
10430
10431           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10432
10433           content_element = change->target_content.e[xx][yy];
10434           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10435                                               ce_value, ce_score);
10436
10437           CreateElementFromChange(ex, ey, target_element);
10438
10439           something_has_changed = TRUE;
10440
10441           // for symmetry reasons, freeze newly created border elements
10442           if (ex != x || ey != y)
10443             Stop[ex][ey] = TRUE;        // no more moving in this frame
10444         }
10445       }
10446
10447       if (something_has_changed)
10448       {
10449         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10450         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10451       }
10452     }
10453   }
10454   else
10455   {
10456     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10457                                         ce_value, ce_score);
10458
10459     if (element == EL_DIAGONAL_GROWING ||
10460         element == EL_DIAGONAL_SHRINKING)
10461     {
10462       target_element = Store[x][y];
10463
10464       Store[x][y] = EL_EMPTY;
10465     }
10466
10467     CreateElementFromChange(x, y, target_element);
10468
10469     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10470     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10471   }
10472
10473   // this uses direct change before indirect change
10474   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10475
10476   return TRUE;
10477 }
10478
10479 static void HandleElementChange(int x, int y, int page)
10480 {
10481   int element = MovingOrBlocked2Element(x, y);
10482   struct ElementInfo *ei = &element_info[element];
10483   struct ElementChangeInfo *change = &ei->change_page[page];
10484   boolean handle_action_before_change = FALSE;
10485
10486 #ifdef DEBUG
10487   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10488       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10489   {
10490     printf("\n\n");
10491     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10492            x, y, element, element_info[element].token_name);
10493     printf("HandleElementChange(): This should never happen!\n");
10494     printf("\n\n");
10495   }
10496 #endif
10497
10498   // this can happen with classic bombs on walkable, changing elements
10499   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10500   {
10501     return;
10502   }
10503
10504   if (ChangeDelay[x][y] == 0)           // initialize element change
10505   {
10506     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10507
10508     if (change->can_change)
10509     {
10510       // !!! not clear why graphic animation should be reset at all here !!!
10511       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10512       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10513
10514       /*
10515         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10516
10517         When using an animation frame delay of 1 (this only happens with
10518         "sp_zonk.moving.left/right" in the classic graphics), the default
10519         (non-moving) animation shows wrong animation frames (while the
10520         moving animation, like "sp_zonk.moving.left/right", is correct,
10521         so this graphical bug never shows up with the classic graphics).
10522         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10523         be drawn instead of the correct frames 0,1,2,3. This is caused by
10524         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10525         an element change: First when the change delay ("ChangeDelay[][]")
10526         counter has reached zero after decrementing, then a second time in
10527         the next frame (after "GfxFrame[][]" was already incremented) when
10528         "ChangeDelay[][]" is reset to the initial delay value again.
10529
10530         This causes frame 0 to be drawn twice, while the last frame won't
10531         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10532
10533         As some animations may already be cleverly designed around this bug
10534         (at least the "Snake Bite" snake tail animation does this), it cannot
10535         simply be fixed here without breaking such existing animations.
10536         Unfortunately, it cannot easily be detected if a graphics set was
10537         designed "before" or "after" the bug was fixed. As a workaround,
10538         a new graphics set option "game.graphics_engine_version" was added
10539         to be able to specify the game's major release version for which the
10540         graphics set was designed, which can then be used to decide if the
10541         bugfix should be used (version 4 and above) or not (version 3 or
10542         below, or if no version was specified at all, as with old sets).
10543
10544         (The wrong/fixed animation frames can be tested with the test level set
10545         "test_gfxframe" and level "000", which contains a specially prepared
10546         custom element at level position (x/y) == (11/9) which uses the zonk
10547         animation mentioned above. Using "game.graphics_engine_version: 4"
10548         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10549         This can also be seen from the debug output for this test element.)
10550       */
10551
10552       // when a custom element is about to change (for example by change delay),
10553       // do not reset graphic animation when the custom element is moving
10554       if (game.graphics_engine_version < 4 &&
10555           !IS_MOVING(x, y))
10556       {
10557         ResetGfxAnimation(x, y);
10558         ResetRandomAnimationValue(x, y);
10559       }
10560
10561       if (change->pre_change_function)
10562         change->pre_change_function(x, y);
10563     }
10564   }
10565
10566   ChangeDelay[x][y]--;
10567
10568   if (ChangeDelay[x][y] != 0)           // continue element change
10569   {
10570     if (change->can_change)
10571     {
10572       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10573
10574       if (IS_ANIMATED(graphic))
10575         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10576
10577       if (change->change_function)
10578         change->change_function(x, y);
10579     }
10580   }
10581   else                                  // finish element change
10582   {
10583     if (ChangePage[x][y] != -1)         // remember page from delayed change
10584     {
10585       page = ChangePage[x][y];
10586       ChangePage[x][y] = -1;
10587
10588       change = &ei->change_page[page];
10589     }
10590
10591     if (IS_MOVING(x, y))                // never change a running system ;-)
10592     {
10593       ChangeDelay[x][y] = 1;            // try change after next move step
10594       ChangePage[x][y] = page;          // remember page to use for change
10595
10596       return;
10597     }
10598
10599     // special case: set new level random seed before changing element
10600     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10601       handle_action_before_change = TRUE;
10602
10603     if (change->has_action && handle_action_before_change)
10604       ExecuteCustomElementAction(x, y, element, page);
10605
10606     if (change->can_change)
10607     {
10608       if (ChangeElement(x, y, element, page))
10609       {
10610         if (change->post_change_function)
10611           change->post_change_function(x, y);
10612       }
10613     }
10614
10615     if (change->has_action && !handle_action_before_change)
10616       ExecuteCustomElementAction(x, y, element, page);
10617   }
10618 }
10619
10620 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10621                                               int trigger_element,
10622                                               int trigger_event,
10623                                               int trigger_player,
10624                                               int trigger_side,
10625                                               int trigger_page)
10626 {
10627   boolean change_done_any = FALSE;
10628   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10629   int i;
10630
10631   if (!(trigger_events[trigger_element][trigger_event]))
10632     return FALSE;
10633
10634   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10635
10636   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10637   {
10638     int element = EL_CUSTOM_START + i;
10639     boolean change_done = FALSE;
10640     int p;
10641
10642     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10643         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10644       continue;
10645
10646     for (p = 0; p < element_info[element].num_change_pages; p++)
10647     {
10648       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10649
10650       if (change->can_change_or_has_action &&
10651           change->has_event[trigger_event] &&
10652           change->trigger_side & trigger_side &&
10653           change->trigger_player & trigger_player &&
10654           change->trigger_page & trigger_page_bits &&
10655           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10656       {
10657         change->actual_trigger_element = trigger_element;
10658         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10659         change->actual_trigger_player_bits = trigger_player;
10660         change->actual_trigger_side = trigger_side;
10661         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10662         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10663
10664         if ((change->can_change && !change_done) || change->has_action)
10665         {
10666           int x, y;
10667
10668           SCAN_PLAYFIELD(x, y)
10669           {
10670             if (Feld[x][y] == element)
10671             {
10672               if (change->can_change && !change_done)
10673               {
10674                 // if element already changed in this frame, not only prevent
10675                 // another element change (checked in ChangeElement()), but
10676                 // also prevent additional element actions for this element
10677
10678                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10679                     !level.use_action_after_change_bug)
10680                   continue;
10681
10682                 ChangeDelay[x][y] = 1;
10683                 ChangeEvent[x][y] = trigger_event;
10684
10685                 HandleElementChange(x, y, p);
10686               }
10687               else if (change->has_action)
10688               {
10689                 // if element already changed in this frame, not only prevent
10690                 // another element change (checked in ChangeElement()), but
10691                 // also prevent additional element actions for this element
10692
10693                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10694                     !level.use_action_after_change_bug)
10695                   continue;
10696
10697                 ExecuteCustomElementAction(x, y, element, p);
10698                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10699               }
10700             }
10701           }
10702
10703           if (change->can_change)
10704           {
10705             change_done = TRUE;
10706             change_done_any = TRUE;
10707           }
10708         }
10709       }
10710     }
10711   }
10712
10713   RECURSION_LOOP_DETECTION_END();
10714
10715   return change_done_any;
10716 }
10717
10718 static boolean CheckElementChangeExt(int x, int y,
10719                                      int element,
10720                                      int trigger_element,
10721                                      int trigger_event,
10722                                      int trigger_player,
10723                                      int trigger_side)
10724 {
10725   boolean change_done = FALSE;
10726   int p;
10727
10728   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10729       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10730     return FALSE;
10731
10732   if (Feld[x][y] == EL_BLOCKED)
10733   {
10734     Blocked2Moving(x, y, &x, &y);
10735     element = Feld[x][y];
10736   }
10737
10738   // check if element has already changed or is about to change after moving
10739   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10740        Feld[x][y] != element) ||
10741
10742       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10743        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10744         ChangePage[x][y] != -1)))
10745     return FALSE;
10746
10747   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10748
10749   for (p = 0; p < element_info[element].num_change_pages; p++)
10750   {
10751     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10752
10753     /* check trigger element for all events where the element that is checked
10754        for changing interacts with a directly adjacent element -- this is
10755        different to element changes that affect other elements to change on the
10756        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10757     boolean check_trigger_element =
10758       (trigger_event == CE_TOUCHING_X ||
10759        trigger_event == CE_HITTING_X ||
10760        trigger_event == CE_HIT_BY_X ||
10761        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10762
10763     if (change->can_change_or_has_action &&
10764         change->has_event[trigger_event] &&
10765         change->trigger_side & trigger_side &&
10766         change->trigger_player & trigger_player &&
10767         (!check_trigger_element ||
10768          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10769     {
10770       change->actual_trigger_element = trigger_element;
10771       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10772       change->actual_trigger_player_bits = trigger_player;
10773       change->actual_trigger_side = trigger_side;
10774       change->actual_trigger_ce_value = CustomValue[x][y];
10775       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10776
10777       // special case: trigger element not at (x,y) position for some events
10778       if (check_trigger_element)
10779       {
10780         static struct
10781         {
10782           int dx, dy;
10783         } move_xy[] =
10784           {
10785             {  0,  0 },
10786             { -1,  0 },
10787             { +1,  0 },
10788             {  0,  0 },
10789             {  0, -1 },
10790             {  0,  0 }, { 0, 0 }, { 0, 0 },
10791             {  0, +1 }
10792           };
10793
10794         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10795         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10796
10797         change->actual_trigger_ce_value = CustomValue[xx][yy];
10798         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10799       }
10800
10801       if (change->can_change && !change_done)
10802       {
10803         ChangeDelay[x][y] = 1;
10804         ChangeEvent[x][y] = trigger_event;
10805
10806         HandleElementChange(x, y, p);
10807
10808         change_done = TRUE;
10809       }
10810       else if (change->has_action)
10811       {
10812         ExecuteCustomElementAction(x, y, element, p);
10813         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10814       }
10815     }
10816   }
10817
10818   RECURSION_LOOP_DETECTION_END();
10819
10820   return change_done;
10821 }
10822
10823 static void PlayPlayerSound(struct PlayerInfo *player)
10824 {
10825   int jx = player->jx, jy = player->jy;
10826   int sound_element = player->artwork_element;
10827   int last_action = player->last_action_waiting;
10828   int action = player->action_waiting;
10829
10830   if (player->is_waiting)
10831   {
10832     if (action != last_action)
10833       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10834     else
10835       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10836   }
10837   else
10838   {
10839     if (action != last_action)
10840       StopSound(element_info[sound_element].sound[last_action]);
10841
10842     if (last_action == ACTION_SLEEPING)
10843       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10844   }
10845 }
10846
10847 static void PlayAllPlayersSound(void)
10848 {
10849   int i;
10850
10851   for (i = 0; i < MAX_PLAYERS; i++)
10852     if (stored_player[i].active)
10853       PlayPlayerSound(&stored_player[i]);
10854 }
10855
10856 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10857 {
10858   boolean last_waiting = player->is_waiting;
10859   int move_dir = player->MovDir;
10860
10861   player->dir_waiting = move_dir;
10862   player->last_action_waiting = player->action_waiting;
10863
10864   if (is_waiting)
10865   {
10866     if (!last_waiting)          // not waiting -> waiting
10867     {
10868       player->is_waiting = TRUE;
10869
10870       player->frame_counter_bored =
10871         FrameCounter +
10872         game.player_boring_delay_fixed +
10873         GetSimpleRandom(game.player_boring_delay_random);
10874       player->frame_counter_sleeping =
10875         FrameCounter +
10876         game.player_sleeping_delay_fixed +
10877         GetSimpleRandom(game.player_sleeping_delay_random);
10878
10879       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10880     }
10881
10882     if (game.player_sleeping_delay_fixed +
10883         game.player_sleeping_delay_random > 0 &&
10884         player->anim_delay_counter == 0 &&
10885         player->post_delay_counter == 0 &&
10886         FrameCounter >= player->frame_counter_sleeping)
10887       player->is_sleeping = TRUE;
10888     else if (game.player_boring_delay_fixed +
10889              game.player_boring_delay_random > 0 &&
10890              FrameCounter >= player->frame_counter_bored)
10891       player->is_bored = TRUE;
10892
10893     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10894                               player->is_bored ? ACTION_BORING :
10895                               ACTION_WAITING);
10896
10897     if (player->is_sleeping && player->use_murphy)
10898     {
10899       // special case for sleeping Murphy when leaning against non-free tile
10900
10901       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10902           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10903            !IS_MOVING(player->jx - 1, player->jy)))
10904         move_dir = MV_LEFT;
10905       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10906                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10907                 !IS_MOVING(player->jx + 1, player->jy)))
10908         move_dir = MV_RIGHT;
10909       else
10910         player->is_sleeping = FALSE;
10911
10912       player->dir_waiting = move_dir;
10913     }
10914
10915     if (player->is_sleeping)
10916     {
10917       if (player->num_special_action_sleeping > 0)
10918       {
10919         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10920         {
10921           int last_special_action = player->special_action_sleeping;
10922           int num_special_action = player->num_special_action_sleeping;
10923           int special_action =
10924             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10925              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10926              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10927              last_special_action + 1 : ACTION_SLEEPING);
10928           int special_graphic =
10929             el_act_dir2img(player->artwork_element, special_action, move_dir);
10930
10931           player->anim_delay_counter =
10932             graphic_info[special_graphic].anim_delay_fixed +
10933             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10934           player->post_delay_counter =
10935             graphic_info[special_graphic].post_delay_fixed +
10936             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10937
10938           player->special_action_sleeping = special_action;
10939         }
10940
10941         if (player->anim_delay_counter > 0)
10942         {
10943           player->action_waiting = player->special_action_sleeping;
10944           player->anim_delay_counter--;
10945         }
10946         else if (player->post_delay_counter > 0)
10947         {
10948           player->post_delay_counter--;
10949         }
10950       }
10951     }
10952     else if (player->is_bored)
10953     {
10954       if (player->num_special_action_bored > 0)
10955       {
10956         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10957         {
10958           int special_action =
10959             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10960           int special_graphic =
10961             el_act_dir2img(player->artwork_element, special_action, move_dir);
10962
10963           player->anim_delay_counter =
10964             graphic_info[special_graphic].anim_delay_fixed +
10965             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10966           player->post_delay_counter =
10967             graphic_info[special_graphic].post_delay_fixed +
10968             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10969
10970           player->special_action_bored = special_action;
10971         }
10972
10973         if (player->anim_delay_counter > 0)
10974         {
10975           player->action_waiting = player->special_action_bored;
10976           player->anim_delay_counter--;
10977         }
10978         else if (player->post_delay_counter > 0)
10979         {
10980           player->post_delay_counter--;
10981         }
10982       }
10983     }
10984   }
10985   else if (last_waiting)        // waiting -> not waiting
10986   {
10987     player->is_waiting = FALSE;
10988     player->is_bored = FALSE;
10989     player->is_sleeping = FALSE;
10990
10991     player->frame_counter_bored = -1;
10992     player->frame_counter_sleeping = -1;
10993
10994     player->anim_delay_counter = 0;
10995     player->post_delay_counter = 0;
10996
10997     player->dir_waiting = player->MovDir;
10998     player->action_waiting = ACTION_DEFAULT;
10999
11000     player->special_action_bored = ACTION_DEFAULT;
11001     player->special_action_sleeping = ACTION_DEFAULT;
11002   }
11003 }
11004
11005 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11006 {
11007   if ((!player->is_moving  && player->was_moving) ||
11008       (player->MovPos == 0 && player->was_moving) ||
11009       (player->is_snapping && !player->was_snapping) ||
11010       (player->is_dropping && !player->was_dropping))
11011   {
11012     if (!CheckSaveEngineSnapshotToList())
11013       return;
11014
11015     player->was_moving = FALSE;
11016     player->was_snapping = TRUE;
11017     player->was_dropping = TRUE;
11018   }
11019   else
11020   {
11021     if (player->is_moving)
11022       player->was_moving = TRUE;
11023
11024     if (!player->is_snapping)
11025       player->was_snapping = FALSE;
11026
11027     if (!player->is_dropping)
11028       player->was_dropping = FALSE;
11029   }
11030 }
11031
11032 static void CheckSingleStepMode(struct PlayerInfo *player)
11033 {
11034   if (tape.single_step && tape.recording && !tape.pausing)
11035   {
11036     /* as it is called "single step mode", just return to pause mode when the
11037        player stopped moving after one tile (or never starts moving at all) */
11038     if (!player->is_moving &&
11039         !player->is_pushing &&
11040         !player->is_dropping_pressed)
11041     {
11042       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11043       SnapField(player, 0, 0);                  // stop snapping
11044     }
11045   }
11046
11047   CheckSaveEngineSnapshot(player);
11048 }
11049
11050 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11051 {
11052   int left      = player_action & JOY_LEFT;
11053   int right     = player_action & JOY_RIGHT;
11054   int up        = player_action & JOY_UP;
11055   int down      = player_action & JOY_DOWN;
11056   int button1   = player_action & JOY_BUTTON_1;
11057   int button2   = player_action & JOY_BUTTON_2;
11058   int dx        = (left ? -1 : right ? 1 : 0);
11059   int dy        = (up   ? -1 : down  ? 1 : 0);
11060
11061   if (!player->active || tape.pausing)
11062     return 0;
11063
11064   if (player_action)
11065   {
11066     if (button1)
11067       SnapField(player, dx, dy);
11068     else
11069     {
11070       if (button2)
11071         DropElement(player);
11072
11073       MovePlayer(player, dx, dy);
11074     }
11075
11076     CheckSingleStepMode(player);
11077
11078     SetPlayerWaiting(player, FALSE);
11079
11080     return player_action;
11081   }
11082   else
11083   {
11084     // no actions for this player (no input at player's configured device)
11085
11086     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11087     SnapField(player, 0, 0);
11088     CheckGravityMovementWhenNotMoving(player);
11089
11090     if (player->MovPos == 0)
11091       SetPlayerWaiting(player, TRUE);
11092
11093     if (player->MovPos == 0)    // needed for tape.playing
11094       player->is_moving = FALSE;
11095
11096     player->is_dropping = FALSE;
11097     player->is_dropping_pressed = FALSE;
11098     player->drop_pressed_delay = 0;
11099
11100     CheckSingleStepMode(player);
11101
11102     return 0;
11103   }
11104 }
11105
11106 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11107                                          byte *tape_action)
11108 {
11109   if (!tape.use_mouse)
11110     return;
11111
11112   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11113   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11114   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11115 }
11116
11117 static void SetTapeActionFromMouseAction(byte *tape_action,
11118                                          struct MouseActionInfo *mouse_action)
11119 {
11120   if (!tape.use_mouse)
11121     return;
11122
11123   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11124   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11125   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11126 }
11127
11128 static void CheckLevelSolved(void)
11129 {
11130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11131   {
11132     if (game_em.level_solved &&
11133         !game_em.game_over)                             // game won
11134     {
11135       LevelSolved();
11136
11137       game_em.game_over = TRUE;
11138
11139       game.all_players_gone = TRUE;
11140     }
11141
11142     if (game_em.game_over)                              // game lost
11143       game.all_players_gone = TRUE;
11144   }
11145   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11146   {
11147     if (game_sp.level_solved &&
11148         !game_sp.game_over)                             // game won
11149     {
11150       LevelSolved();
11151
11152       game_sp.game_over = TRUE;
11153
11154       game.all_players_gone = TRUE;
11155     }
11156
11157     if (game_sp.game_over)                              // game lost
11158       game.all_players_gone = TRUE;
11159   }
11160   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11161   {
11162     if (game_mm.level_solved &&
11163         !game_mm.game_over)                             // game won
11164     {
11165       LevelSolved();
11166
11167       game_mm.game_over = TRUE;
11168
11169       game.all_players_gone = TRUE;
11170     }
11171
11172     if (game_mm.game_over)                              // game lost
11173       game.all_players_gone = TRUE;
11174   }
11175 }
11176
11177 static void CheckLevelTime(void)
11178 {
11179   int i;
11180
11181   if (TimeFrames >= FRAMES_PER_SECOND)
11182   {
11183     TimeFrames = 0;
11184     TapeTime++;
11185
11186     for (i = 0; i < MAX_PLAYERS; i++)
11187     {
11188       struct PlayerInfo *player = &stored_player[i];
11189
11190       if (SHIELD_ON(player))
11191       {
11192         player->shield_normal_time_left--;
11193
11194         if (player->shield_deadly_time_left > 0)
11195           player->shield_deadly_time_left--;
11196       }
11197     }
11198
11199     if (!game.LevelSolved && !level.use_step_counter)
11200     {
11201       TimePlayed++;
11202
11203       if (TimeLeft > 0)
11204       {
11205         TimeLeft--;
11206
11207         if (TimeLeft <= 10 && setup.time_limit)
11208           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11209
11210         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11211            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11212
11213         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11214
11215         if (!TimeLeft && setup.time_limit)
11216         {
11217           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11218             level.native_em_level->lev->killed_out_of_time = TRUE;
11219           else
11220             for (i = 0; i < MAX_PLAYERS; i++)
11221               KillPlayer(&stored_player[i]);
11222         }
11223       }
11224       else if (game.no_time_limit && !game.all_players_gone)
11225       {
11226         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11227       }
11228
11229       level.native_em_level->lev->time =
11230         (game.no_time_limit ? TimePlayed : TimeLeft);
11231     }
11232
11233     if (tape.recording || tape.playing)
11234       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11235   }
11236
11237   if (tape.recording || tape.playing)
11238     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11239
11240   UpdateAndDisplayGameControlValues();
11241 }
11242
11243 void AdvanceFrameAndPlayerCounters(int player_nr)
11244 {
11245   int i;
11246
11247   // advance frame counters (global frame counter and time frame counter)
11248   FrameCounter++;
11249   TimeFrames++;
11250
11251   // advance player counters (counters for move delay, move animation etc.)
11252   for (i = 0; i < MAX_PLAYERS; i++)
11253   {
11254     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11255     int move_delay_value = stored_player[i].move_delay_value;
11256     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11257
11258     if (!advance_player_counters)       // not all players may be affected
11259       continue;
11260
11261     if (move_frames == 0)       // less than one move per game frame
11262     {
11263       int stepsize = TILEX / move_delay_value;
11264       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11265       int count = (stored_player[i].is_moving ?
11266                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11267
11268       if (count % delay == 0)
11269         move_frames = 1;
11270     }
11271
11272     stored_player[i].Frame += move_frames;
11273
11274     if (stored_player[i].MovPos != 0)
11275       stored_player[i].StepFrame += move_frames;
11276
11277     if (stored_player[i].move_delay > 0)
11278       stored_player[i].move_delay--;
11279
11280     // due to bugs in previous versions, counter must count up, not down
11281     if (stored_player[i].push_delay != -1)
11282       stored_player[i].push_delay++;
11283
11284     if (stored_player[i].drop_delay > 0)
11285       stored_player[i].drop_delay--;
11286
11287     if (stored_player[i].is_dropping_pressed)
11288       stored_player[i].drop_pressed_delay++;
11289   }
11290 }
11291
11292 void StartGameActions(boolean init_network_game, boolean record_tape,
11293                       int random_seed)
11294 {
11295   unsigned int new_random_seed = InitRND(random_seed);
11296
11297   if (record_tape)
11298     TapeStartRecording(new_random_seed);
11299
11300   if (init_network_game)
11301   {
11302     SendToServer_LevelFile();
11303     SendToServer_StartPlaying();
11304
11305     return;
11306   }
11307
11308   InitGame();
11309 }
11310
11311 static void GameActionsExt(void)
11312 {
11313 #if 0
11314   static unsigned int game_frame_delay = 0;
11315 #endif
11316   unsigned int game_frame_delay_value;
11317   byte *recorded_player_action;
11318   byte summarized_player_action = 0;
11319   byte tape_action[MAX_PLAYERS];
11320   int i;
11321
11322   // detect endless loops, caused by custom element programming
11323   if (recursion_loop_detected && recursion_loop_depth == 0)
11324   {
11325     char *message = getStringCat3("Internal Error! Element ",
11326                                   EL_NAME(recursion_loop_element),
11327                                   " caused endless loop! Quit the game?");
11328
11329     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11330           EL_NAME(recursion_loop_element));
11331
11332     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11333
11334     recursion_loop_detected = FALSE;    // if game should be continued
11335
11336     free(message);
11337
11338     return;
11339   }
11340
11341   if (game.restart_level)
11342     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11343
11344   CheckLevelSolved();
11345
11346   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11347     GameWon();
11348
11349   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11350     TapeStop();
11351
11352   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11353     return;
11354
11355   game_frame_delay_value =
11356     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11357
11358   if (tape.playing && tape.warp_forward && !tape.pausing)
11359     game_frame_delay_value = 0;
11360
11361   SetVideoFrameDelay(game_frame_delay_value);
11362
11363 #if 0
11364 #if 0
11365   // ---------- main game synchronization point ----------
11366
11367   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11368
11369   printf("::: skip == %d\n", skip);
11370
11371 #else
11372   // ---------- main game synchronization point ----------
11373
11374   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11375 #endif
11376 #endif
11377
11378   if (network_playing && !network_player_action_received)
11379   {
11380     // try to get network player actions in time
11381
11382     // last chance to get network player actions without main loop delay
11383     HandleNetworking();
11384
11385     // game was quit by network peer
11386     if (game_status != GAME_MODE_PLAYING)
11387       return;
11388
11389     // check if network player actions still missing and game still running
11390     if (!network_player_action_received && !checkGameEnded())
11391       return;           // failed to get network player actions in time
11392
11393     // do not yet reset "network_player_action_received" (for tape.pausing)
11394   }
11395
11396   if (tape.pausing)
11397     return;
11398
11399   // at this point we know that we really continue executing the game
11400
11401   network_player_action_received = FALSE;
11402
11403   // when playing tape, read previously recorded player input from tape data
11404   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11405
11406   local_player->effective_mouse_action = local_player->mouse_action;
11407
11408   if (recorded_player_action != NULL)
11409     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11410                                  recorded_player_action);
11411
11412   // TapePlayAction() may return NULL when toggling to "pause before death"
11413   if (tape.pausing)
11414     return;
11415
11416   if (tape.set_centered_player)
11417   {
11418     game.centered_player_nr_next = tape.centered_player_nr_next;
11419     game.set_centered_player = TRUE;
11420   }
11421
11422   for (i = 0; i < MAX_PLAYERS; i++)
11423   {
11424     summarized_player_action |= stored_player[i].action;
11425
11426     if (!network_playing && (game.team_mode || tape.playing))
11427       stored_player[i].effective_action = stored_player[i].action;
11428   }
11429
11430   if (network_playing && !checkGameEnded())
11431     SendToServer_MovePlayer(summarized_player_action);
11432
11433   // summarize all actions at local players mapped input device position
11434   // (this allows using different input devices in single player mode)
11435   if (!network.enabled && !game.team_mode)
11436     stored_player[map_player_action[local_player->index_nr]].effective_action =
11437       summarized_player_action;
11438
11439   if (tape.recording &&
11440       setup.team_mode &&
11441       setup.input_on_focus &&
11442       game.centered_player_nr != -1)
11443   {
11444     for (i = 0; i < MAX_PLAYERS; i++)
11445       stored_player[i].effective_action =
11446         (i == game.centered_player_nr ? summarized_player_action : 0);
11447   }
11448
11449   if (recorded_player_action != NULL)
11450     for (i = 0; i < MAX_PLAYERS; i++)
11451       stored_player[i].effective_action = recorded_player_action[i];
11452
11453   for (i = 0; i < MAX_PLAYERS; i++)
11454   {
11455     tape_action[i] = stored_player[i].effective_action;
11456
11457     /* (this may happen in the RND game engine if a player was not present on
11458        the playfield on level start, but appeared later from a custom element */
11459     if (setup.team_mode &&
11460         tape.recording &&
11461         tape_action[i] &&
11462         !tape.player_participates[i])
11463       tape.player_participates[i] = TRUE;
11464   }
11465
11466   SetTapeActionFromMouseAction(tape_action,
11467                                &local_player->effective_mouse_action);
11468
11469   // only record actions from input devices, but not programmed actions
11470   if (tape.recording)
11471     TapeRecordAction(tape_action);
11472
11473 #if USE_NEW_PLAYER_ASSIGNMENTS
11474   // !!! also map player actions in single player mode !!!
11475   // if (game.team_mode)
11476   if (1)
11477   {
11478     byte mapped_action[MAX_PLAYERS];
11479
11480 #if DEBUG_PLAYER_ACTIONS
11481     printf(":::");
11482     for (i = 0; i < MAX_PLAYERS; i++)
11483       printf(" %d, ", stored_player[i].effective_action);
11484 #endif
11485
11486     for (i = 0; i < MAX_PLAYERS; i++)
11487       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11488
11489     for (i = 0; i < MAX_PLAYERS; i++)
11490       stored_player[i].effective_action = mapped_action[i];
11491
11492 #if DEBUG_PLAYER_ACTIONS
11493     printf(" =>");
11494     for (i = 0; i < MAX_PLAYERS; i++)
11495       printf(" %d, ", stored_player[i].effective_action);
11496     printf("\n");
11497 #endif
11498   }
11499 #if DEBUG_PLAYER_ACTIONS
11500   else
11501   {
11502     printf(":::");
11503     for (i = 0; i < MAX_PLAYERS; i++)
11504       printf(" %d, ", stored_player[i].effective_action);
11505     printf("\n");
11506   }
11507 #endif
11508 #endif
11509
11510   for (i = 0; i < MAX_PLAYERS; i++)
11511   {
11512     // allow engine snapshot in case of changed movement attempt
11513     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11514         (stored_player[i].effective_action & KEY_MOTION))
11515       game.snapshot.changed_action = TRUE;
11516
11517     // allow engine snapshot in case of snapping/dropping attempt
11518     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11519         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11520       game.snapshot.changed_action = TRUE;
11521
11522     game.snapshot.last_action[i] = stored_player[i].effective_action;
11523   }
11524
11525   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11526   {
11527     GameActions_EM_Main();
11528   }
11529   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11530   {
11531     GameActions_SP_Main();
11532   }
11533   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11534   {
11535     GameActions_MM_Main();
11536   }
11537   else
11538   {
11539     GameActions_RND_Main();
11540   }
11541
11542   BlitScreenToBitmap(backbuffer);
11543
11544   CheckLevelSolved();
11545   CheckLevelTime();
11546
11547   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11548
11549   if (global.show_frames_per_second)
11550   {
11551     static unsigned int fps_counter = 0;
11552     static int fps_frames = 0;
11553     unsigned int fps_delay_ms = Counter() - fps_counter;
11554
11555     fps_frames++;
11556
11557     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11558     {
11559       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11560
11561       fps_frames = 0;
11562       fps_counter = Counter();
11563
11564       // always draw FPS to screen after FPS value was updated
11565       redraw_mask |= REDRAW_FPS;
11566     }
11567
11568     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11569     if (GetDrawDeactivationMask() == REDRAW_NONE)
11570       redraw_mask |= REDRAW_FPS;
11571   }
11572 }
11573
11574 static void GameActions_CheckSaveEngineSnapshot(void)
11575 {
11576   if (!game.snapshot.save_snapshot)
11577     return;
11578
11579   // clear flag for saving snapshot _before_ saving snapshot
11580   game.snapshot.save_snapshot = FALSE;
11581
11582   SaveEngineSnapshotToList();
11583 }
11584
11585 void GameActions(void)
11586 {
11587   GameActionsExt();
11588
11589   GameActions_CheckSaveEngineSnapshot();
11590 }
11591
11592 void GameActions_EM_Main(void)
11593 {
11594   byte effective_action[MAX_PLAYERS];
11595   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11596   int i;
11597
11598   for (i = 0; i < MAX_PLAYERS; i++)
11599     effective_action[i] = stored_player[i].effective_action;
11600
11601   GameActions_EM(effective_action, warp_mode);
11602 }
11603
11604 void GameActions_SP_Main(void)
11605 {
11606   byte effective_action[MAX_PLAYERS];
11607   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11608   int i;
11609
11610   for (i = 0; i < MAX_PLAYERS; i++)
11611     effective_action[i] = stored_player[i].effective_action;
11612
11613   GameActions_SP(effective_action, warp_mode);
11614
11615   for (i = 0; i < MAX_PLAYERS; i++)
11616   {
11617     if (stored_player[i].force_dropping)
11618       stored_player[i].action |= KEY_BUTTON_DROP;
11619
11620     stored_player[i].force_dropping = FALSE;
11621   }
11622 }
11623
11624 void GameActions_MM_Main(void)
11625 {
11626   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11627
11628   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11629 }
11630
11631 void GameActions_RND_Main(void)
11632 {
11633   GameActions_RND();
11634 }
11635
11636 void GameActions_RND(void)
11637 {
11638   int magic_wall_x = 0, magic_wall_y = 0;
11639   int i, x, y, element, graphic, last_gfx_frame;
11640
11641   InitPlayfieldScanModeVars();
11642
11643   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11644   {
11645     SCAN_PLAYFIELD(x, y)
11646     {
11647       ChangeCount[x][y] = 0;
11648       ChangeEvent[x][y] = -1;
11649     }
11650   }
11651
11652   if (game.set_centered_player)
11653   {
11654     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11655
11656     // switching to "all players" only possible if all players fit to screen
11657     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11658     {
11659       game.centered_player_nr_next = game.centered_player_nr;
11660       game.set_centered_player = FALSE;
11661     }
11662
11663     // do not switch focus to non-existing (or non-active) player
11664     if (game.centered_player_nr_next >= 0 &&
11665         !stored_player[game.centered_player_nr_next].active)
11666     {
11667       game.centered_player_nr_next = game.centered_player_nr;
11668       game.set_centered_player = FALSE;
11669     }
11670   }
11671
11672   if (game.set_centered_player &&
11673       ScreenMovPos == 0)        // screen currently aligned at tile position
11674   {
11675     int sx, sy;
11676
11677     if (game.centered_player_nr_next == -1)
11678     {
11679       setScreenCenteredToAllPlayers(&sx, &sy);
11680     }
11681     else
11682     {
11683       sx = stored_player[game.centered_player_nr_next].jx;
11684       sy = stored_player[game.centered_player_nr_next].jy;
11685     }
11686
11687     game.centered_player_nr = game.centered_player_nr_next;
11688     game.set_centered_player = FALSE;
11689
11690     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11691     DrawGameDoorValues();
11692   }
11693
11694   for (i = 0; i < MAX_PLAYERS; i++)
11695   {
11696     int actual_player_action = stored_player[i].effective_action;
11697
11698 #if 1
11699     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11700        - rnd_equinox_tetrachloride 048
11701        - rnd_equinox_tetrachloride_ii 096
11702        - rnd_emanuel_schmieg 002
11703        - doctor_sloan_ww 001, 020
11704     */
11705     if (stored_player[i].MovPos == 0)
11706       CheckGravityMovement(&stored_player[i]);
11707 #endif
11708
11709     // overwrite programmed action with tape action
11710     if (stored_player[i].programmed_action)
11711       actual_player_action = stored_player[i].programmed_action;
11712
11713     PlayerActions(&stored_player[i], actual_player_action);
11714
11715     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11716   }
11717
11718   ScrollScreen(NULL, SCROLL_GO_ON);
11719
11720   /* for backwards compatibility, the following code emulates a fixed bug that
11721      occured when pushing elements (causing elements that just made their last
11722      pushing step to already (if possible) make their first falling step in the
11723      same game frame, which is bad); this code is also needed to use the famous
11724      "spring push bug" which is used in older levels and might be wanted to be
11725      used also in newer levels, but in this case the buggy pushing code is only
11726      affecting the "spring" element and no other elements */
11727
11728   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11729   {
11730     for (i = 0; i < MAX_PLAYERS; i++)
11731     {
11732       struct PlayerInfo *player = &stored_player[i];
11733       int x = player->jx;
11734       int y = player->jy;
11735
11736       if (player->active && player->is_pushing && player->is_moving &&
11737           IS_MOVING(x, y) &&
11738           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11739            Feld[x][y] == EL_SPRING))
11740       {
11741         ContinueMoving(x, y);
11742
11743         // continue moving after pushing (this is actually a bug)
11744         if (!IS_MOVING(x, y))
11745           Stop[x][y] = FALSE;
11746       }
11747     }
11748   }
11749
11750   SCAN_PLAYFIELD(x, y)
11751   {
11752     Last[x][y] = Feld[x][y];
11753
11754     ChangeCount[x][y] = 0;
11755     ChangeEvent[x][y] = -1;
11756
11757     // this must be handled before main playfield loop
11758     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11759     {
11760       MovDelay[x][y]--;
11761       if (MovDelay[x][y] <= 0)
11762         RemoveField(x, y);
11763     }
11764
11765     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11766     {
11767       MovDelay[x][y]--;
11768       if (MovDelay[x][y] <= 0)
11769       {
11770         RemoveField(x, y);
11771         TEST_DrawLevelField(x, y);
11772
11773         TestIfElementTouchesCustomElement(x, y);        // for empty space
11774       }
11775     }
11776
11777 #if DEBUG
11778     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11779     {
11780       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11781       printf("GameActions(): This should never happen!\n");
11782
11783       ChangePage[x][y] = -1;
11784     }
11785 #endif
11786
11787     Stop[x][y] = FALSE;
11788     if (WasJustMoving[x][y] > 0)
11789       WasJustMoving[x][y]--;
11790     if (WasJustFalling[x][y] > 0)
11791       WasJustFalling[x][y]--;
11792     if (CheckCollision[x][y] > 0)
11793       CheckCollision[x][y]--;
11794     if (CheckImpact[x][y] > 0)
11795       CheckImpact[x][y]--;
11796
11797     GfxFrame[x][y]++;
11798
11799     /* reset finished pushing action (not done in ContinueMoving() to allow
11800        continuous pushing animation for elements with zero push delay) */
11801     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11802     {
11803       ResetGfxAnimation(x, y);
11804       TEST_DrawLevelField(x, y);
11805     }
11806
11807 #if DEBUG
11808     if (IS_BLOCKED(x, y))
11809     {
11810       int oldx, oldy;
11811
11812       Blocked2Moving(x, y, &oldx, &oldy);
11813       if (!IS_MOVING(oldx, oldy))
11814       {
11815         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11816         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11817         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11818         printf("GameActions(): This should never happen!\n");
11819       }
11820     }
11821 #endif
11822   }
11823
11824   SCAN_PLAYFIELD(x, y)
11825   {
11826     element = Feld[x][y];
11827     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11828     last_gfx_frame = GfxFrame[x][y];
11829
11830     ResetGfxFrame(x, y);
11831
11832     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11833       DrawLevelGraphicAnimation(x, y, graphic);
11834
11835     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11836         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11837       ResetRandomAnimationValue(x, y);
11838
11839     SetRandomAnimationValue(x, y);
11840
11841     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11842
11843     if (IS_INACTIVE(element))
11844     {
11845       if (IS_ANIMATED(graphic))
11846         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11847
11848       continue;
11849     }
11850
11851     // this may take place after moving, so 'element' may have changed
11852     if (IS_CHANGING(x, y) &&
11853         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11854     {
11855       int page = element_info[element].event_page_nr[CE_DELAY];
11856
11857       HandleElementChange(x, y, page);
11858
11859       element = Feld[x][y];
11860       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11861     }
11862
11863     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11864     {
11865       StartMoving(x, y);
11866
11867       element = Feld[x][y];
11868       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11869
11870       if (IS_ANIMATED(graphic) &&
11871           !IS_MOVING(x, y) &&
11872           !Stop[x][y])
11873         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11874
11875       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11876         TEST_DrawTwinkleOnField(x, y);
11877     }
11878     else if (element == EL_ACID)
11879     {
11880       if (!Stop[x][y])
11881         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11882     }
11883     else if ((element == EL_EXIT_OPEN ||
11884               element == EL_EM_EXIT_OPEN ||
11885               element == EL_SP_EXIT_OPEN ||
11886               element == EL_STEEL_EXIT_OPEN ||
11887               element == EL_EM_STEEL_EXIT_OPEN ||
11888               element == EL_SP_TERMINAL ||
11889               element == EL_SP_TERMINAL_ACTIVE ||
11890               element == EL_EXTRA_TIME ||
11891               element == EL_SHIELD_NORMAL ||
11892               element == EL_SHIELD_DEADLY) &&
11893              IS_ANIMATED(graphic))
11894       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11895     else if (IS_MOVING(x, y))
11896       ContinueMoving(x, y);
11897     else if (IS_ACTIVE_BOMB(element))
11898       CheckDynamite(x, y);
11899     else if (element == EL_AMOEBA_GROWING)
11900       AmoebeWaechst(x, y);
11901     else if (element == EL_AMOEBA_SHRINKING)
11902       AmoebaDisappearing(x, y);
11903
11904 #if !USE_NEW_AMOEBA_CODE
11905     else if (IS_AMOEBALIVE(element))
11906       AmoebeAbleger(x, y);
11907 #endif
11908
11909     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11910       Life(x, y);
11911     else if (element == EL_EXIT_CLOSED)
11912       CheckExit(x, y);
11913     else if (element == EL_EM_EXIT_CLOSED)
11914       CheckExitEM(x, y);
11915     else if (element == EL_STEEL_EXIT_CLOSED)
11916       CheckExitSteel(x, y);
11917     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11918       CheckExitSteelEM(x, y);
11919     else if (element == EL_SP_EXIT_CLOSED)
11920       CheckExitSP(x, y);
11921     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11922              element == EL_EXPANDABLE_STEELWALL_GROWING)
11923       MauerWaechst(x, y);
11924     else if (element == EL_EXPANDABLE_WALL ||
11925              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11926              element == EL_EXPANDABLE_WALL_VERTICAL ||
11927              element == EL_EXPANDABLE_WALL_ANY ||
11928              element == EL_BD_EXPANDABLE_WALL)
11929       MauerAbleger(x, y);
11930     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11931              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11932              element == EL_EXPANDABLE_STEELWALL_ANY)
11933       MauerAblegerStahl(x, y);
11934     else if (element == EL_FLAMES)
11935       CheckForDragon(x, y);
11936     else if (element == EL_EXPLOSION)
11937       ; // drawing of correct explosion animation is handled separately
11938     else if (element == EL_ELEMENT_SNAPPING ||
11939              element == EL_DIAGONAL_SHRINKING ||
11940              element == EL_DIAGONAL_GROWING)
11941     {
11942       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11943
11944       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11945     }
11946     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11947       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11948
11949     if (IS_BELT_ACTIVE(element))
11950       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11951
11952     if (game.magic_wall_active)
11953     {
11954       int jx = local_player->jx, jy = local_player->jy;
11955
11956       // play the element sound at the position nearest to the player
11957       if ((element == EL_MAGIC_WALL_FULL ||
11958            element == EL_MAGIC_WALL_ACTIVE ||
11959            element == EL_MAGIC_WALL_EMPTYING ||
11960            element == EL_BD_MAGIC_WALL_FULL ||
11961            element == EL_BD_MAGIC_WALL_ACTIVE ||
11962            element == EL_BD_MAGIC_WALL_EMPTYING ||
11963            element == EL_DC_MAGIC_WALL_FULL ||
11964            element == EL_DC_MAGIC_WALL_ACTIVE ||
11965            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11966           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11967       {
11968         magic_wall_x = x;
11969         magic_wall_y = y;
11970       }
11971     }
11972   }
11973
11974 #if USE_NEW_AMOEBA_CODE
11975   // new experimental amoeba growth stuff
11976   if (!(FrameCounter % 8))
11977   {
11978     static unsigned int random = 1684108901;
11979
11980     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11981     {
11982       x = RND(lev_fieldx);
11983       y = RND(lev_fieldy);
11984       element = Feld[x][y];
11985
11986       if (!IS_PLAYER(x,y) &&
11987           (element == EL_EMPTY ||
11988            CAN_GROW_INTO(element) ||
11989            element == EL_QUICKSAND_EMPTY ||
11990            element == EL_QUICKSAND_FAST_EMPTY ||
11991            element == EL_ACID_SPLASH_LEFT ||
11992            element == EL_ACID_SPLASH_RIGHT))
11993       {
11994         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11995             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11996             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11997             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11998           Feld[x][y] = EL_AMOEBA_DROP;
11999       }
12000
12001       random = random * 129 + 1;
12002     }
12003   }
12004 #endif
12005
12006   game.explosions_delayed = FALSE;
12007
12008   SCAN_PLAYFIELD(x, y)
12009   {
12010     element = Feld[x][y];
12011
12012     if (ExplodeField[x][y])
12013       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12014     else if (element == EL_EXPLOSION)
12015       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12016
12017     ExplodeField[x][y] = EX_TYPE_NONE;
12018   }
12019
12020   game.explosions_delayed = TRUE;
12021
12022   if (game.magic_wall_active)
12023   {
12024     if (!(game.magic_wall_time_left % 4))
12025     {
12026       int element = Feld[magic_wall_x][magic_wall_y];
12027
12028       if (element == EL_BD_MAGIC_WALL_FULL ||
12029           element == EL_BD_MAGIC_WALL_ACTIVE ||
12030           element == EL_BD_MAGIC_WALL_EMPTYING)
12031         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12032       else if (element == EL_DC_MAGIC_WALL_FULL ||
12033                element == EL_DC_MAGIC_WALL_ACTIVE ||
12034                element == EL_DC_MAGIC_WALL_EMPTYING)
12035         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12036       else
12037         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12038     }
12039
12040     if (game.magic_wall_time_left > 0)
12041     {
12042       game.magic_wall_time_left--;
12043
12044       if (!game.magic_wall_time_left)
12045       {
12046         SCAN_PLAYFIELD(x, y)
12047         {
12048           element = Feld[x][y];
12049
12050           if (element == EL_MAGIC_WALL_ACTIVE ||
12051               element == EL_MAGIC_WALL_FULL)
12052           {
12053             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12054             TEST_DrawLevelField(x, y);
12055           }
12056           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12057                    element == EL_BD_MAGIC_WALL_FULL)
12058           {
12059             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12060             TEST_DrawLevelField(x, y);
12061           }
12062           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12063                    element == EL_DC_MAGIC_WALL_FULL)
12064           {
12065             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12066             TEST_DrawLevelField(x, y);
12067           }
12068         }
12069
12070         game.magic_wall_active = FALSE;
12071       }
12072     }
12073   }
12074
12075   if (game.light_time_left > 0)
12076   {
12077     game.light_time_left--;
12078
12079     if (game.light_time_left == 0)
12080       RedrawAllLightSwitchesAndInvisibleElements();
12081   }
12082
12083   if (game.timegate_time_left > 0)
12084   {
12085     game.timegate_time_left--;
12086
12087     if (game.timegate_time_left == 0)
12088       CloseAllOpenTimegates();
12089   }
12090
12091   if (game.lenses_time_left > 0)
12092   {
12093     game.lenses_time_left--;
12094
12095     if (game.lenses_time_left == 0)
12096       RedrawAllInvisibleElementsForLenses();
12097   }
12098
12099   if (game.magnify_time_left > 0)
12100   {
12101     game.magnify_time_left--;
12102
12103     if (game.magnify_time_left == 0)
12104       RedrawAllInvisibleElementsForMagnifier();
12105   }
12106
12107   for (i = 0; i < MAX_PLAYERS; i++)
12108   {
12109     struct PlayerInfo *player = &stored_player[i];
12110
12111     if (SHIELD_ON(player))
12112     {
12113       if (player->shield_deadly_time_left)
12114         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12115       else if (player->shield_normal_time_left)
12116         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12117     }
12118   }
12119
12120 #if USE_DELAYED_GFX_REDRAW
12121   SCAN_PLAYFIELD(x, y)
12122   {
12123     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12124     {
12125       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12126          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12127
12128       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12129         DrawLevelField(x, y);
12130
12131       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12132         DrawLevelFieldCrumbled(x, y);
12133
12134       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12135         DrawLevelFieldCrumbledNeighbours(x, y);
12136
12137       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12138         DrawTwinkleOnField(x, y);
12139     }
12140
12141     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12142   }
12143 #endif
12144
12145   DrawAllPlayers();
12146   PlayAllPlayersSound();
12147
12148   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12149   {
12150     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12151
12152     local_player->show_envelope = 0;
12153   }
12154
12155   // use random number generator in every frame to make it less predictable
12156   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12157     RND(1);
12158 }
12159
12160 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12161 {
12162   int min_x = x, min_y = y, max_x = x, max_y = y;
12163   int i;
12164
12165   for (i = 0; i < MAX_PLAYERS; i++)
12166   {
12167     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12168
12169     if (!stored_player[i].active || &stored_player[i] == player)
12170       continue;
12171
12172     min_x = MIN(min_x, jx);
12173     min_y = MIN(min_y, jy);
12174     max_x = MAX(max_x, jx);
12175     max_y = MAX(max_y, jy);
12176   }
12177
12178   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12179 }
12180
12181 static boolean AllPlayersInVisibleScreen(void)
12182 {
12183   int i;
12184
12185   for (i = 0; i < MAX_PLAYERS; i++)
12186   {
12187     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12188
12189     if (!stored_player[i].active)
12190       continue;
12191
12192     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12193       return FALSE;
12194   }
12195
12196   return TRUE;
12197 }
12198
12199 void ScrollLevel(int dx, int dy)
12200 {
12201   int scroll_offset = 2 * TILEX_VAR;
12202   int x, y;
12203
12204   BlitBitmap(drawto_field, drawto_field,
12205              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12206              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12207              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12208              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12209              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12210              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12211
12212   if (dx != 0)
12213   {
12214     x = (dx == 1 ? BX1 : BX2);
12215     for (y = BY1; y <= BY2; y++)
12216       DrawScreenField(x, y);
12217   }
12218
12219   if (dy != 0)
12220   {
12221     y = (dy == 1 ? BY1 : BY2);
12222     for (x = BX1; x <= BX2; x++)
12223       DrawScreenField(x, y);
12224   }
12225
12226   redraw_mask |= REDRAW_FIELD;
12227 }
12228
12229 static boolean canFallDown(struct PlayerInfo *player)
12230 {
12231   int jx = player->jx, jy = player->jy;
12232
12233   return (IN_LEV_FIELD(jx, jy + 1) &&
12234           (IS_FREE(jx, jy + 1) ||
12235            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12236           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12237           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12238 }
12239
12240 static boolean canPassField(int x, int y, int move_dir)
12241 {
12242   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12243   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12244   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12245   int nextx = x + dx;
12246   int nexty = y + dy;
12247   int element = Feld[x][y];
12248
12249   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12250           !CAN_MOVE(element) &&
12251           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12252           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12253           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12254 }
12255
12256 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12257 {
12258   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12259   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12260   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12261   int newx = x + dx;
12262   int newy = y + dy;
12263
12264   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12265           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12266           (IS_DIGGABLE(Feld[newx][newy]) ||
12267            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12268            canPassField(newx, newy, move_dir)));
12269 }
12270
12271 static void CheckGravityMovement(struct PlayerInfo *player)
12272 {
12273   if (player->gravity && !player->programmed_action)
12274   {
12275     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12276     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12277     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12278     int jx = player->jx, jy = player->jy;
12279     boolean player_is_moving_to_valid_field =
12280       (!player_is_snapping &&
12281        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12282         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12283     boolean player_can_fall_down = canFallDown(player);
12284
12285     if (player_can_fall_down &&
12286         !player_is_moving_to_valid_field)
12287       player->programmed_action = MV_DOWN;
12288   }
12289 }
12290
12291 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12292 {
12293   return CheckGravityMovement(player);
12294
12295   if (player->gravity && !player->programmed_action)
12296   {
12297     int jx = player->jx, jy = player->jy;
12298     boolean field_under_player_is_free =
12299       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12300     boolean player_is_standing_on_valid_field =
12301       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12302        (IS_WALKABLE(Feld[jx][jy]) &&
12303         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12304
12305     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12306       player->programmed_action = MV_DOWN;
12307   }
12308 }
12309
12310 /*
12311   MovePlayerOneStep()
12312   -----------------------------------------------------------------------------
12313   dx, dy:               direction (non-diagonal) to try to move the player to
12314   real_dx, real_dy:     direction as read from input device (can be diagonal)
12315 */
12316
12317 boolean MovePlayerOneStep(struct PlayerInfo *player,
12318                           int dx, int dy, int real_dx, int real_dy)
12319 {
12320   int jx = player->jx, jy = player->jy;
12321   int new_jx = jx + dx, new_jy = jy + dy;
12322   int can_move;
12323   boolean player_can_move = !player->cannot_move;
12324
12325   if (!player->active || (!dx && !dy))
12326     return MP_NO_ACTION;
12327
12328   player->MovDir = (dx < 0 ? MV_LEFT :
12329                     dx > 0 ? MV_RIGHT :
12330                     dy < 0 ? MV_UP :
12331                     dy > 0 ? MV_DOWN :  MV_NONE);
12332
12333   if (!IN_LEV_FIELD(new_jx, new_jy))
12334     return MP_NO_ACTION;
12335
12336   if (!player_can_move)
12337   {
12338     if (player->MovPos == 0)
12339     {
12340       player->is_moving = FALSE;
12341       player->is_digging = FALSE;
12342       player->is_collecting = FALSE;
12343       player->is_snapping = FALSE;
12344       player->is_pushing = FALSE;
12345     }
12346   }
12347
12348   if (!network.enabled && game.centered_player_nr == -1 &&
12349       !AllPlayersInSight(player, new_jx, new_jy))
12350     return MP_NO_ACTION;
12351
12352   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12353   if (can_move != MP_MOVING)
12354     return can_move;
12355
12356   // check if DigField() has caused relocation of the player
12357   if (player->jx != jx || player->jy != jy)
12358     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12359
12360   StorePlayer[jx][jy] = 0;
12361   player->last_jx = jx;
12362   player->last_jy = jy;
12363   player->jx = new_jx;
12364   player->jy = new_jy;
12365   StorePlayer[new_jx][new_jy] = player->element_nr;
12366
12367   if (player->move_delay_value_next != -1)
12368   {
12369     player->move_delay_value = player->move_delay_value_next;
12370     player->move_delay_value_next = -1;
12371   }
12372
12373   player->MovPos =
12374     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12375
12376   player->step_counter++;
12377
12378   PlayerVisit[jx][jy] = FrameCounter;
12379
12380   player->is_moving = TRUE;
12381
12382 #if 1
12383   // should better be called in MovePlayer(), but this breaks some tapes
12384   ScrollPlayer(player, SCROLL_INIT);
12385 #endif
12386
12387   return MP_MOVING;
12388 }
12389
12390 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12391 {
12392   int jx = player->jx, jy = player->jy;
12393   int old_jx = jx, old_jy = jy;
12394   int moved = MP_NO_ACTION;
12395
12396   if (!player->active)
12397     return FALSE;
12398
12399   if (!dx && !dy)
12400   {
12401     if (player->MovPos == 0)
12402     {
12403       player->is_moving = FALSE;
12404       player->is_digging = FALSE;
12405       player->is_collecting = FALSE;
12406       player->is_snapping = FALSE;
12407       player->is_pushing = FALSE;
12408     }
12409
12410     return FALSE;
12411   }
12412
12413   if (player->move_delay > 0)
12414     return FALSE;
12415
12416   player->move_delay = -1;              // set to "uninitialized" value
12417
12418   // store if player is automatically moved to next field
12419   player->is_auto_moving = (player->programmed_action != MV_NONE);
12420
12421   // remove the last programmed player action
12422   player->programmed_action = 0;
12423
12424   if (player->MovPos)
12425   {
12426     // should only happen if pre-1.2 tape recordings are played
12427     // this is only for backward compatibility
12428
12429     int original_move_delay_value = player->move_delay_value;
12430
12431 #if DEBUG
12432     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12433            tape.counter);
12434 #endif
12435
12436     // scroll remaining steps with finest movement resolution
12437     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12438
12439     while (player->MovPos)
12440     {
12441       ScrollPlayer(player, SCROLL_GO_ON);
12442       ScrollScreen(NULL, SCROLL_GO_ON);
12443
12444       AdvanceFrameAndPlayerCounters(player->index_nr);
12445
12446       DrawAllPlayers();
12447       BackToFront_WithFrameDelay(0);
12448     }
12449
12450     player->move_delay_value = original_move_delay_value;
12451   }
12452
12453   player->is_active = FALSE;
12454
12455   if (player->last_move_dir & MV_HORIZONTAL)
12456   {
12457     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12458       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12459   }
12460   else
12461   {
12462     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12463       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12464   }
12465
12466   if (!moved && !player->is_active)
12467   {
12468     player->is_moving = FALSE;
12469     player->is_digging = FALSE;
12470     player->is_collecting = FALSE;
12471     player->is_snapping = FALSE;
12472     player->is_pushing = FALSE;
12473   }
12474
12475   jx = player->jx;
12476   jy = player->jy;
12477
12478   if (moved & MP_MOVING && !ScreenMovPos &&
12479       (player->index_nr == game.centered_player_nr ||
12480        game.centered_player_nr == -1))
12481   {
12482     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12483     int offset = game.scroll_delay_value;
12484
12485     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12486     {
12487       // actual player has left the screen -- scroll in that direction
12488       if (jx != old_jx)         // player has moved horizontally
12489         scroll_x += (jx - old_jx);
12490       else                      // player has moved vertically
12491         scroll_y += (jy - old_jy);
12492     }
12493     else
12494     {
12495       if (jx != old_jx)         // player has moved horizontally
12496       {
12497         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12498             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12499           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12500
12501         // don't scroll over playfield boundaries
12502         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12503           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12504
12505         // don't scroll more than one field at a time
12506         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12507
12508         // don't scroll against the player's moving direction
12509         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12510             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12511           scroll_x = old_scroll_x;
12512       }
12513       else                      // player has moved vertically
12514       {
12515         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12516             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12517           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12518
12519         // don't scroll over playfield boundaries
12520         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12521           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12522
12523         // don't scroll more than one field at a time
12524         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12525
12526         // don't scroll against the player's moving direction
12527         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12528             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12529           scroll_y = old_scroll_y;
12530       }
12531     }
12532
12533     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12534     {
12535       if (!network.enabled && game.centered_player_nr == -1 &&
12536           !AllPlayersInVisibleScreen())
12537       {
12538         scroll_x = old_scroll_x;
12539         scroll_y = old_scroll_y;
12540       }
12541       else
12542       {
12543         ScrollScreen(player, SCROLL_INIT);
12544         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12545       }
12546     }
12547   }
12548
12549   player->StepFrame = 0;
12550
12551   if (moved & MP_MOVING)
12552   {
12553     if (old_jx != jx && old_jy == jy)
12554       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12555     else if (old_jx == jx && old_jy != jy)
12556       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12557
12558     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12559
12560     player->last_move_dir = player->MovDir;
12561     player->is_moving = TRUE;
12562     player->is_snapping = FALSE;
12563     player->is_switching = FALSE;
12564     player->is_dropping = FALSE;
12565     player->is_dropping_pressed = FALSE;
12566     player->drop_pressed_delay = 0;
12567
12568 #if 0
12569     // should better be called here than above, but this breaks some tapes
12570     ScrollPlayer(player, SCROLL_INIT);
12571 #endif
12572   }
12573   else
12574   {
12575     CheckGravityMovementWhenNotMoving(player);
12576
12577     player->is_moving = FALSE;
12578
12579     /* at this point, the player is allowed to move, but cannot move right now
12580        (e.g. because of something blocking the way) -- ensure that the player
12581        is also allowed to move in the next frame (in old versions before 3.1.1,
12582        the player was forced to wait again for eight frames before next try) */
12583
12584     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12585       player->move_delay = 0;   // allow direct movement in the next frame
12586   }
12587
12588   if (player->move_delay == -1)         // not yet initialized by DigField()
12589     player->move_delay = player->move_delay_value;
12590
12591   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12592   {
12593     TestIfPlayerTouchesBadThing(jx, jy);
12594     TestIfPlayerTouchesCustomElement(jx, jy);
12595   }
12596
12597   if (!player->active)
12598     RemovePlayer(player);
12599
12600   return moved;
12601 }
12602
12603 void ScrollPlayer(struct PlayerInfo *player, int mode)
12604 {
12605   int jx = player->jx, jy = player->jy;
12606   int last_jx = player->last_jx, last_jy = player->last_jy;
12607   int move_stepsize = TILEX / player->move_delay_value;
12608
12609   if (!player->active)
12610     return;
12611
12612   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12613     return;
12614
12615   if (mode == SCROLL_INIT)
12616   {
12617     player->actual_frame_counter = FrameCounter;
12618     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12619
12620     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12621         Feld[last_jx][last_jy] == EL_EMPTY)
12622     {
12623       int last_field_block_delay = 0;   // start with no blocking at all
12624       int block_delay_adjustment = player->block_delay_adjustment;
12625
12626       // if player blocks last field, add delay for exactly one move
12627       if (player->block_last_field)
12628       {
12629         last_field_block_delay += player->move_delay_value;
12630
12631         // when blocking enabled, prevent moving up despite gravity
12632         if (player->gravity && player->MovDir == MV_UP)
12633           block_delay_adjustment = -1;
12634       }
12635
12636       // add block delay adjustment (also possible when not blocking)
12637       last_field_block_delay += block_delay_adjustment;
12638
12639       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12640       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12641     }
12642
12643     if (player->MovPos != 0)    // player has not yet reached destination
12644       return;
12645   }
12646   else if (!FrameReached(&player->actual_frame_counter, 1))
12647     return;
12648
12649   if (player->MovPos != 0)
12650   {
12651     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12652     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12653
12654     // before DrawPlayer() to draw correct player graphic for this case
12655     if (player->MovPos == 0)
12656       CheckGravityMovement(player);
12657   }
12658
12659   if (player->MovPos == 0)      // player reached destination field
12660   {
12661     if (player->move_delay_reset_counter > 0)
12662     {
12663       player->move_delay_reset_counter--;
12664
12665       if (player->move_delay_reset_counter == 0)
12666       {
12667         // continue with normal speed after quickly moving through gate
12668         HALVE_PLAYER_SPEED(player);
12669
12670         // be able to make the next move without delay
12671         player->move_delay = 0;
12672       }
12673     }
12674
12675     player->last_jx = jx;
12676     player->last_jy = jy;
12677
12678     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12679         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12680         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12681         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12682         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12683         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12684         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12685         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12686     {
12687       ExitPlayer(player);
12688
12689       if (game.players_still_needed == 0 &&
12690           (game.friends_still_needed == 0 ||
12691            IS_SP_ELEMENT(Feld[jx][jy])))
12692         LevelSolved();
12693     }
12694
12695     // this breaks one level: "machine", level 000
12696     {
12697       int move_direction = player->MovDir;
12698       int enter_side = MV_DIR_OPPOSITE(move_direction);
12699       int leave_side = move_direction;
12700       int old_jx = last_jx;
12701       int old_jy = last_jy;
12702       int old_element = Feld[old_jx][old_jy];
12703       int new_element = Feld[jx][jy];
12704
12705       if (IS_CUSTOM_ELEMENT(old_element))
12706         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12707                                    CE_LEFT_BY_PLAYER,
12708                                    player->index_bit, leave_side);
12709
12710       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12711                                           CE_PLAYER_LEAVES_X,
12712                                           player->index_bit, leave_side);
12713
12714       if (IS_CUSTOM_ELEMENT(new_element))
12715         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12716                                    player->index_bit, enter_side);
12717
12718       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12719                                           CE_PLAYER_ENTERS_X,
12720                                           player->index_bit, enter_side);
12721
12722       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12723                                         CE_MOVE_OF_X, move_direction);
12724     }
12725
12726     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12727     {
12728       TestIfPlayerTouchesBadThing(jx, jy);
12729       TestIfPlayerTouchesCustomElement(jx, jy);
12730
12731       /* needed because pushed element has not yet reached its destination,
12732          so it would trigger a change event at its previous field location */
12733       if (!player->is_pushing)
12734         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12735
12736       if (!player->active)
12737         RemovePlayer(player);
12738     }
12739
12740     if (!game.LevelSolved && level.use_step_counter)
12741     {
12742       int i;
12743
12744       TimePlayed++;
12745
12746       if (TimeLeft > 0)
12747       {
12748         TimeLeft--;
12749
12750         if (TimeLeft <= 10 && setup.time_limit)
12751           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12752
12753         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12754
12755         DisplayGameControlValues();
12756
12757         if (!TimeLeft && setup.time_limit)
12758           for (i = 0; i < MAX_PLAYERS; i++)
12759             KillPlayer(&stored_player[i]);
12760       }
12761       else if (game.no_time_limit && !game.all_players_gone)
12762       {
12763         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12764
12765         DisplayGameControlValues();
12766       }
12767     }
12768
12769     if (tape.single_step && tape.recording && !tape.pausing &&
12770         !player->programmed_action)
12771       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12772
12773     if (!player->programmed_action)
12774       CheckSaveEngineSnapshot(player);
12775   }
12776 }
12777
12778 void ScrollScreen(struct PlayerInfo *player, int mode)
12779 {
12780   static unsigned int screen_frame_counter = 0;
12781
12782   if (mode == SCROLL_INIT)
12783   {
12784     // set scrolling step size according to actual player's moving speed
12785     ScrollStepSize = TILEX / player->move_delay_value;
12786
12787     screen_frame_counter = FrameCounter;
12788     ScreenMovDir = player->MovDir;
12789     ScreenMovPos = player->MovPos;
12790     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12791     return;
12792   }
12793   else if (!FrameReached(&screen_frame_counter, 1))
12794     return;
12795
12796   if (ScreenMovPos)
12797   {
12798     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12799     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12800     redraw_mask |= REDRAW_FIELD;
12801   }
12802   else
12803     ScreenMovDir = MV_NONE;
12804 }
12805
12806 void TestIfPlayerTouchesCustomElement(int x, int y)
12807 {
12808   static int xy[4][2] =
12809   {
12810     { 0, -1 },
12811     { -1, 0 },
12812     { +1, 0 },
12813     { 0, +1 }
12814   };
12815   static int trigger_sides[4][2] =
12816   {
12817     // center side       border side
12818     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12819     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12820     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12821     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12822   };
12823   static int touch_dir[4] =
12824   {
12825     MV_LEFT | MV_RIGHT,
12826     MV_UP   | MV_DOWN,
12827     MV_UP   | MV_DOWN,
12828     MV_LEFT | MV_RIGHT
12829   };
12830   int center_element = Feld[x][y];      // should always be non-moving!
12831   int i;
12832
12833   for (i = 0; i < NUM_DIRECTIONS; i++)
12834   {
12835     int xx = x + xy[i][0];
12836     int yy = y + xy[i][1];
12837     int center_side = trigger_sides[i][0];
12838     int border_side = trigger_sides[i][1];
12839     int border_element;
12840
12841     if (!IN_LEV_FIELD(xx, yy))
12842       continue;
12843
12844     if (IS_PLAYER(x, y))                // player found at center element
12845     {
12846       struct PlayerInfo *player = PLAYERINFO(x, y);
12847
12848       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12849         border_element = Feld[xx][yy];          // may be moving!
12850       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12851         border_element = Feld[xx][yy];
12852       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12853         border_element = MovingOrBlocked2Element(xx, yy);
12854       else
12855         continue;               // center and border element do not touch
12856
12857       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12858                                  player->index_bit, border_side);
12859       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12860                                           CE_PLAYER_TOUCHES_X,
12861                                           player->index_bit, border_side);
12862
12863       {
12864         /* use player element that is initially defined in the level playfield,
12865            not the player element that corresponds to the runtime player number
12866            (example: a level that contains EL_PLAYER_3 as the only player would
12867            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12868         int player_element = PLAYERINFO(x, y)->initial_element;
12869
12870         CheckElementChangeBySide(xx, yy, border_element, player_element,
12871                                  CE_TOUCHING_X, border_side);
12872       }
12873     }
12874     else if (IS_PLAYER(xx, yy))         // player found at border element
12875     {
12876       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12877
12878       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12879       {
12880         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12881           continue;             // center and border element do not touch
12882       }
12883
12884       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12885                                  player->index_bit, center_side);
12886       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12887                                           CE_PLAYER_TOUCHES_X,
12888                                           player->index_bit, center_side);
12889
12890       {
12891         /* use player element that is initially defined in the level playfield,
12892            not the player element that corresponds to the runtime player number
12893            (example: a level that contains EL_PLAYER_3 as the only player would
12894            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12895         int player_element = PLAYERINFO(xx, yy)->initial_element;
12896
12897         CheckElementChangeBySide(x, y, center_element, player_element,
12898                                  CE_TOUCHING_X, center_side);
12899       }
12900
12901       break;
12902     }
12903   }
12904 }
12905
12906 void TestIfElementTouchesCustomElement(int x, int y)
12907 {
12908   static int xy[4][2] =
12909   {
12910     { 0, -1 },
12911     { -1, 0 },
12912     { +1, 0 },
12913     { 0, +1 }
12914   };
12915   static int trigger_sides[4][2] =
12916   {
12917     // center side      border side
12918     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12919     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12920     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12921     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12922   };
12923   static int touch_dir[4] =
12924   {
12925     MV_LEFT | MV_RIGHT,
12926     MV_UP   | MV_DOWN,
12927     MV_UP   | MV_DOWN,
12928     MV_LEFT | MV_RIGHT
12929   };
12930   boolean change_center_element = FALSE;
12931   int center_element = Feld[x][y];      // should always be non-moving!
12932   int border_element_old[NUM_DIRECTIONS];
12933   int i;
12934
12935   for (i = 0; i < NUM_DIRECTIONS; i++)
12936   {
12937     int xx = x + xy[i][0];
12938     int yy = y + xy[i][1];
12939     int border_element;
12940
12941     border_element_old[i] = -1;
12942
12943     if (!IN_LEV_FIELD(xx, yy))
12944       continue;
12945
12946     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12947       border_element = Feld[xx][yy];    // may be moving!
12948     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12949       border_element = Feld[xx][yy];
12950     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12951       border_element = MovingOrBlocked2Element(xx, yy);
12952     else
12953       continue;                 // center and border element do not touch
12954
12955     border_element_old[i] = border_element;
12956   }
12957
12958   for (i = 0; i < NUM_DIRECTIONS; i++)
12959   {
12960     int xx = x + xy[i][0];
12961     int yy = y + xy[i][1];
12962     int center_side = trigger_sides[i][0];
12963     int border_element = border_element_old[i];
12964
12965     if (border_element == -1)
12966       continue;
12967
12968     // check for change of border element
12969     CheckElementChangeBySide(xx, yy, border_element, center_element,
12970                              CE_TOUCHING_X, center_side);
12971
12972     // (center element cannot be player, so we dont have to check this here)
12973   }
12974
12975   for (i = 0; i < NUM_DIRECTIONS; i++)
12976   {
12977     int xx = x + xy[i][0];
12978     int yy = y + xy[i][1];
12979     int border_side = trigger_sides[i][1];
12980     int border_element = border_element_old[i];
12981
12982     if (border_element == -1)
12983       continue;
12984
12985     // check for change of center element (but change it only once)
12986     if (!change_center_element)
12987       change_center_element =
12988         CheckElementChangeBySide(x, y, center_element, border_element,
12989                                  CE_TOUCHING_X, border_side);
12990
12991     if (IS_PLAYER(xx, yy))
12992     {
12993       /* use player element that is initially defined in the level playfield,
12994          not the player element that corresponds to the runtime player number
12995          (example: a level that contains EL_PLAYER_3 as the only player would
12996          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12997       int player_element = PLAYERINFO(xx, yy)->initial_element;
12998
12999       CheckElementChangeBySide(x, y, center_element, player_element,
13000                                CE_TOUCHING_X, border_side);
13001     }
13002   }
13003 }
13004
13005 void TestIfElementHitsCustomElement(int x, int y, int direction)
13006 {
13007   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13008   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13009   int hitx = x + dx, hity = y + dy;
13010   int hitting_element = Feld[x][y];
13011   int touched_element;
13012
13013   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13014     return;
13015
13016   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13017                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13018
13019   if (IN_LEV_FIELD(hitx, hity))
13020   {
13021     int opposite_direction = MV_DIR_OPPOSITE(direction);
13022     int hitting_side = direction;
13023     int touched_side = opposite_direction;
13024     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13025                           MovDir[hitx][hity] != direction ||
13026                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13027
13028     object_hit = TRUE;
13029
13030     if (object_hit)
13031     {
13032       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13033                                CE_HITTING_X, touched_side);
13034
13035       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13036                                CE_HIT_BY_X, hitting_side);
13037
13038       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13039                                CE_HIT_BY_SOMETHING, opposite_direction);
13040
13041       if (IS_PLAYER(hitx, hity))
13042       {
13043         /* use player element that is initially defined in the level playfield,
13044            not the player element that corresponds to the runtime player number
13045            (example: a level that contains EL_PLAYER_3 as the only player would
13046            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13047         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13048
13049         CheckElementChangeBySide(x, y, hitting_element, player_element,
13050                                  CE_HITTING_X, touched_side);
13051       }
13052     }
13053   }
13054
13055   // "hitting something" is also true when hitting the playfield border
13056   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13057                            CE_HITTING_SOMETHING, direction);
13058 }
13059
13060 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13061 {
13062   int i, kill_x = -1, kill_y = -1;
13063
13064   int bad_element = -1;
13065   static int test_xy[4][2] =
13066   {
13067     { 0, -1 },
13068     { -1, 0 },
13069     { +1, 0 },
13070     { 0, +1 }
13071   };
13072   static int test_dir[4] =
13073   {
13074     MV_UP,
13075     MV_LEFT,
13076     MV_RIGHT,
13077     MV_DOWN
13078   };
13079
13080   for (i = 0; i < NUM_DIRECTIONS; i++)
13081   {
13082     int test_x, test_y, test_move_dir, test_element;
13083
13084     test_x = good_x + test_xy[i][0];
13085     test_y = good_y + test_xy[i][1];
13086
13087     if (!IN_LEV_FIELD(test_x, test_y))
13088       continue;
13089
13090     test_move_dir =
13091       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13092
13093     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13094
13095     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13096        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13097     */
13098     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13099         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13100     {
13101       kill_x = test_x;
13102       kill_y = test_y;
13103       bad_element = test_element;
13104
13105       break;
13106     }
13107   }
13108
13109   if (kill_x != -1 || kill_y != -1)
13110   {
13111     if (IS_PLAYER(good_x, good_y))
13112     {
13113       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13114
13115       if (player->shield_deadly_time_left > 0 &&
13116           !IS_INDESTRUCTIBLE(bad_element))
13117         Bang(kill_x, kill_y);
13118       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13119         KillPlayer(player);
13120     }
13121     else
13122       Bang(good_x, good_y);
13123   }
13124 }
13125
13126 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13127 {
13128   int i, kill_x = -1, kill_y = -1;
13129   int bad_element = Feld[bad_x][bad_y];
13130   static int test_xy[4][2] =
13131   {
13132     { 0, -1 },
13133     { -1, 0 },
13134     { +1, 0 },
13135     { 0, +1 }
13136   };
13137   static int touch_dir[4] =
13138   {
13139     MV_LEFT | MV_RIGHT,
13140     MV_UP   | MV_DOWN,
13141     MV_UP   | MV_DOWN,
13142     MV_LEFT | MV_RIGHT
13143   };
13144   static int test_dir[4] =
13145   {
13146     MV_UP,
13147     MV_LEFT,
13148     MV_RIGHT,
13149     MV_DOWN
13150   };
13151
13152   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13153     return;
13154
13155   for (i = 0; i < NUM_DIRECTIONS; i++)
13156   {
13157     int test_x, test_y, test_move_dir, test_element;
13158
13159     test_x = bad_x + test_xy[i][0];
13160     test_y = bad_y + test_xy[i][1];
13161
13162     if (!IN_LEV_FIELD(test_x, test_y))
13163       continue;
13164
13165     test_move_dir =
13166       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13167
13168     test_element = Feld[test_x][test_y];
13169
13170     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13171        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13172     */
13173     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13174         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13175     {
13176       // good thing is player or penguin that does not move away
13177       if (IS_PLAYER(test_x, test_y))
13178       {
13179         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13180
13181         if (bad_element == EL_ROBOT && player->is_moving)
13182           continue;     // robot does not kill player if he is moving
13183
13184         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13185         {
13186           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13187             continue;           // center and border element do not touch
13188         }
13189
13190         kill_x = test_x;
13191         kill_y = test_y;
13192
13193         break;
13194       }
13195       else if (test_element == EL_PENGUIN)
13196       {
13197         kill_x = test_x;
13198         kill_y = test_y;
13199
13200         break;
13201       }
13202     }
13203   }
13204
13205   if (kill_x != -1 || kill_y != -1)
13206   {
13207     if (IS_PLAYER(kill_x, kill_y))
13208     {
13209       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13210
13211       if (player->shield_deadly_time_left > 0 &&
13212           !IS_INDESTRUCTIBLE(bad_element))
13213         Bang(bad_x, bad_y);
13214       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13215         KillPlayer(player);
13216     }
13217     else
13218       Bang(kill_x, kill_y);
13219   }
13220 }
13221
13222 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13223 {
13224   int bad_element = Feld[bad_x][bad_y];
13225   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13226   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13227   int test_x = bad_x + dx, test_y = bad_y + dy;
13228   int test_move_dir, test_element;
13229   int kill_x = -1, kill_y = -1;
13230
13231   if (!IN_LEV_FIELD(test_x, test_y))
13232     return;
13233
13234   test_move_dir =
13235     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13236
13237   test_element = Feld[test_x][test_y];
13238
13239   if (test_move_dir != bad_move_dir)
13240   {
13241     // good thing can be player or penguin that does not move away
13242     if (IS_PLAYER(test_x, test_y))
13243     {
13244       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13245
13246       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13247          player as being hit when he is moving towards the bad thing, because
13248          the "get hit by" condition would be lost after the player stops) */
13249       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13250         return;         // player moves away from bad thing
13251
13252       kill_x = test_x;
13253       kill_y = test_y;
13254     }
13255     else if (test_element == EL_PENGUIN)
13256     {
13257       kill_x = test_x;
13258       kill_y = test_y;
13259     }
13260   }
13261
13262   if (kill_x != -1 || kill_y != -1)
13263   {
13264     if (IS_PLAYER(kill_x, kill_y))
13265     {
13266       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13267
13268       if (player->shield_deadly_time_left > 0 &&
13269           !IS_INDESTRUCTIBLE(bad_element))
13270         Bang(bad_x, bad_y);
13271       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13272         KillPlayer(player);
13273     }
13274     else
13275       Bang(kill_x, kill_y);
13276   }
13277 }
13278
13279 void TestIfPlayerTouchesBadThing(int x, int y)
13280 {
13281   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13282 }
13283
13284 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13285 {
13286   TestIfGoodThingHitsBadThing(x, y, move_dir);
13287 }
13288
13289 void TestIfBadThingTouchesPlayer(int x, int y)
13290 {
13291   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13292 }
13293
13294 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13295 {
13296   TestIfBadThingHitsGoodThing(x, y, move_dir);
13297 }
13298
13299 void TestIfFriendTouchesBadThing(int x, int y)
13300 {
13301   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13302 }
13303
13304 void TestIfBadThingTouchesFriend(int x, int y)
13305 {
13306   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13307 }
13308
13309 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13310 {
13311   int i, kill_x = bad_x, kill_y = bad_y;
13312   static int xy[4][2] =
13313   {
13314     { 0, -1 },
13315     { -1, 0 },
13316     { +1, 0 },
13317     { 0, +1 }
13318   };
13319
13320   for (i = 0; i < NUM_DIRECTIONS; i++)
13321   {
13322     int x, y, element;
13323
13324     x = bad_x + xy[i][0];
13325     y = bad_y + xy[i][1];
13326     if (!IN_LEV_FIELD(x, y))
13327       continue;
13328
13329     element = Feld[x][y];
13330     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13331         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13332     {
13333       kill_x = x;
13334       kill_y = y;
13335       break;
13336     }
13337   }
13338
13339   if (kill_x != bad_x || kill_y != bad_y)
13340     Bang(bad_x, bad_y);
13341 }
13342
13343 void KillPlayer(struct PlayerInfo *player)
13344 {
13345   int jx = player->jx, jy = player->jy;
13346
13347   if (!player->active)
13348     return;
13349
13350 #if 0
13351   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13352          player->killed, player->active, player->reanimated);
13353 #endif
13354
13355   /* the following code was introduced to prevent an infinite loop when calling
13356      -> Bang()
13357      -> CheckTriggeredElementChangeExt()
13358      -> ExecuteCustomElementAction()
13359      -> KillPlayer()
13360      -> (infinitely repeating the above sequence of function calls)
13361      which occurs when killing the player while having a CE with the setting
13362      "kill player X when explosion of <player X>"; the solution using a new
13363      field "player->killed" was chosen for backwards compatibility, although
13364      clever use of the fields "player->active" etc. would probably also work */
13365 #if 1
13366   if (player->killed)
13367     return;
13368 #endif
13369
13370   player->killed = TRUE;
13371
13372   // remove accessible field at the player's position
13373   Feld[jx][jy] = EL_EMPTY;
13374
13375   // deactivate shield (else Bang()/Explode() would not work right)
13376   player->shield_normal_time_left = 0;
13377   player->shield_deadly_time_left = 0;
13378
13379 #if 0
13380   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13381          player->killed, player->active, player->reanimated);
13382 #endif
13383
13384   Bang(jx, jy);
13385
13386 #if 0
13387   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13388          player->killed, player->active, player->reanimated);
13389 #endif
13390
13391   if (player->reanimated)       // killed player may have been reanimated
13392     player->killed = player->reanimated = FALSE;
13393   else
13394     BuryPlayer(player);
13395 }
13396
13397 static void KillPlayerUnlessEnemyProtected(int x, int y)
13398 {
13399   if (!PLAYER_ENEMY_PROTECTED(x, y))
13400     KillPlayer(PLAYERINFO(x, y));
13401 }
13402
13403 static void KillPlayerUnlessExplosionProtected(int x, int y)
13404 {
13405   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13406     KillPlayer(PLAYERINFO(x, y));
13407 }
13408
13409 void BuryPlayer(struct PlayerInfo *player)
13410 {
13411   int jx = player->jx, jy = player->jy;
13412
13413   if (!player->active)
13414     return;
13415
13416   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13417   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13418
13419   RemovePlayer(player);
13420
13421   player->buried = TRUE;
13422
13423   if (game.all_players_gone)
13424     game.GameOver = TRUE;
13425 }
13426
13427 void RemovePlayer(struct PlayerInfo *player)
13428 {
13429   int jx = player->jx, jy = player->jy;
13430   int i, found = FALSE;
13431
13432   player->present = FALSE;
13433   player->active = FALSE;
13434
13435   if (!ExplodeField[jx][jy])
13436     StorePlayer[jx][jy] = 0;
13437
13438   if (player->is_moving)
13439     TEST_DrawLevelField(player->last_jx, player->last_jy);
13440
13441   for (i = 0; i < MAX_PLAYERS; i++)
13442     if (stored_player[i].active)
13443       found = TRUE;
13444
13445   if (!found)
13446   {
13447     game.all_players_gone = TRUE;
13448     game.GameOver = TRUE;
13449   }
13450
13451   game.exit_x = ZX = jx;
13452   game.exit_y = ZY = jy;
13453 }
13454
13455 void ExitPlayer(struct PlayerInfo *player)
13456 {
13457   DrawPlayer(player);   // needed here only to cleanup last field
13458   RemovePlayer(player);
13459
13460   if (game.players_still_needed > 0)
13461     game.players_still_needed--;
13462 }
13463
13464 static void setFieldForSnapping(int x, int y, int element, int direction)
13465 {
13466   struct ElementInfo *ei = &element_info[element];
13467   int direction_bit = MV_DIR_TO_BIT(direction);
13468   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13469   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13470                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13471
13472   Feld[x][y] = EL_ELEMENT_SNAPPING;
13473   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13474
13475   ResetGfxAnimation(x, y);
13476
13477   GfxElement[x][y] = element;
13478   GfxAction[x][y] = action;
13479   GfxDir[x][y] = direction;
13480   GfxFrame[x][y] = -1;
13481 }
13482
13483 /*
13484   =============================================================================
13485   checkDiagonalPushing()
13486   -----------------------------------------------------------------------------
13487   check if diagonal input device direction results in pushing of object
13488   (by checking if the alternative direction is walkable, diggable, ...)
13489   =============================================================================
13490 */
13491
13492 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13493                                     int x, int y, int real_dx, int real_dy)
13494 {
13495   int jx, jy, dx, dy, xx, yy;
13496
13497   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13498     return TRUE;
13499
13500   // diagonal direction: check alternative direction
13501   jx = player->jx;
13502   jy = player->jy;
13503   dx = x - jx;
13504   dy = y - jy;
13505   xx = jx + (dx == 0 ? real_dx : 0);
13506   yy = jy + (dy == 0 ? real_dy : 0);
13507
13508   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13509 }
13510
13511 /*
13512   =============================================================================
13513   DigField()
13514   -----------------------------------------------------------------------------
13515   x, y:                 field next to player (non-diagonal) to try to dig to
13516   real_dx, real_dy:     direction as read from input device (can be diagonal)
13517   =============================================================================
13518 */
13519
13520 static int DigField(struct PlayerInfo *player,
13521                     int oldx, int oldy, int x, int y,
13522                     int real_dx, int real_dy, int mode)
13523 {
13524   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13525   boolean player_was_pushing = player->is_pushing;
13526   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13527   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13528   int jx = oldx, jy = oldy;
13529   int dx = x - jx, dy = y - jy;
13530   int nextx = x + dx, nexty = y + dy;
13531   int move_direction = (dx == -1 ? MV_LEFT  :
13532                         dx == +1 ? MV_RIGHT :
13533                         dy == -1 ? MV_UP    :
13534                         dy == +1 ? MV_DOWN  : MV_NONE);
13535   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13536   int dig_side = MV_DIR_OPPOSITE(move_direction);
13537   int old_element = Feld[jx][jy];
13538   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13539   int collect_count;
13540
13541   if (is_player)                // function can also be called by EL_PENGUIN
13542   {
13543     if (player->MovPos == 0)
13544     {
13545       player->is_digging = FALSE;
13546       player->is_collecting = FALSE;
13547     }
13548
13549     if (player->MovPos == 0)    // last pushing move finished
13550       player->is_pushing = FALSE;
13551
13552     if (mode == DF_NO_PUSH)     // player just stopped pushing
13553     {
13554       player->is_switching = FALSE;
13555       player->push_delay = -1;
13556
13557       return MP_NO_ACTION;
13558     }
13559   }
13560
13561   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13562     old_element = Back[jx][jy];
13563
13564   // in case of element dropped at player position, check background
13565   else if (Back[jx][jy] != EL_EMPTY &&
13566            game.engine_version >= VERSION_IDENT(2,2,0,0))
13567     old_element = Back[jx][jy];
13568
13569   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13570     return MP_NO_ACTION;        // field has no opening in this direction
13571
13572   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13573     return MP_NO_ACTION;        // field has no opening in this direction
13574
13575   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13576   {
13577     SplashAcid(x, y);
13578
13579     Feld[jx][jy] = player->artwork_element;
13580     InitMovingField(jx, jy, MV_DOWN);
13581     Store[jx][jy] = EL_ACID;
13582     ContinueMoving(jx, jy);
13583     BuryPlayer(player);
13584
13585     return MP_DONT_RUN_INTO;
13586   }
13587
13588   if (player_can_move && DONT_RUN_INTO(element))
13589   {
13590     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13591
13592     return MP_DONT_RUN_INTO;
13593   }
13594
13595   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13596     return MP_NO_ACTION;
13597
13598   collect_count = element_info[element].collect_count_initial;
13599
13600   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13601     return MP_NO_ACTION;
13602
13603   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13604     player_can_move = player_can_move_or_snap;
13605
13606   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13607       game.engine_version >= VERSION_IDENT(2,2,0,0))
13608   {
13609     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13610                                player->index_bit, dig_side);
13611     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13612                                         player->index_bit, dig_side);
13613
13614     if (element == EL_DC_LANDMINE)
13615       Bang(x, y);
13616
13617     if (Feld[x][y] != element)          // field changed by snapping
13618       return MP_ACTION;
13619
13620     return MP_NO_ACTION;
13621   }
13622
13623   if (player->gravity && is_player && !player->is_auto_moving &&
13624       canFallDown(player) && move_direction != MV_DOWN &&
13625       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13626     return MP_NO_ACTION;        // player cannot walk here due to gravity
13627
13628   if (player_can_move &&
13629       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13630   {
13631     int sound_element = SND_ELEMENT(element);
13632     int sound_action = ACTION_WALKING;
13633
13634     if (IS_RND_GATE(element))
13635     {
13636       if (!player->key[RND_GATE_NR(element)])
13637         return MP_NO_ACTION;
13638     }
13639     else if (IS_RND_GATE_GRAY(element))
13640     {
13641       if (!player->key[RND_GATE_GRAY_NR(element)])
13642         return MP_NO_ACTION;
13643     }
13644     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13645     {
13646       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13647         return MP_NO_ACTION;
13648     }
13649     else if (element == EL_EXIT_OPEN ||
13650              element == EL_EM_EXIT_OPEN ||
13651              element == EL_EM_EXIT_OPENING ||
13652              element == EL_STEEL_EXIT_OPEN ||
13653              element == EL_EM_STEEL_EXIT_OPEN ||
13654              element == EL_EM_STEEL_EXIT_OPENING ||
13655              element == EL_SP_EXIT_OPEN ||
13656              element == EL_SP_EXIT_OPENING)
13657     {
13658       sound_action = ACTION_PASSING;    // player is passing exit
13659     }
13660     else if (element == EL_EMPTY)
13661     {
13662       sound_action = ACTION_MOVING;             // nothing to walk on
13663     }
13664
13665     // play sound from background or player, whatever is available
13666     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13667       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13668     else
13669       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13670   }
13671   else if (player_can_move &&
13672            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13673   {
13674     if (!ACCESS_FROM(element, opposite_direction))
13675       return MP_NO_ACTION;      // field not accessible from this direction
13676
13677     if (CAN_MOVE(element))      // only fixed elements can be passed!
13678       return MP_NO_ACTION;
13679
13680     if (IS_EM_GATE(element))
13681     {
13682       if (!player->key[EM_GATE_NR(element)])
13683         return MP_NO_ACTION;
13684     }
13685     else if (IS_EM_GATE_GRAY(element))
13686     {
13687       if (!player->key[EM_GATE_GRAY_NR(element)])
13688         return MP_NO_ACTION;
13689     }
13690     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13691     {
13692       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13693         return MP_NO_ACTION;
13694     }
13695     else if (IS_EMC_GATE(element))
13696     {
13697       if (!player->key[EMC_GATE_NR(element)])
13698         return MP_NO_ACTION;
13699     }
13700     else if (IS_EMC_GATE_GRAY(element))
13701     {
13702       if (!player->key[EMC_GATE_GRAY_NR(element)])
13703         return MP_NO_ACTION;
13704     }
13705     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13706     {
13707       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13708         return MP_NO_ACTION;
13709     }
13710     else if (element == EL_DC_GATE_WHITE ||
13711              element == EL_DC_GATE_WHITE_GRAY ||
13712              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13713     {
13714       if (player->num_white_keys == 0)
13715         return MP_NO_ACTION;
13716
13717       player->num_white_keys--;
13718     }
13719     else if (IS_SP_PORT(element))
13720     {
13721       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13722           element == EL_SP_GRAVITY_PORT_RIGHT ||
13723           element == EL_SP_GRAVITY_PORT_UP ||
13724           element == EL_SP_GRAVITY_PORT_DOWN)
13725         player->gravity = !player->gravity;
13726       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13727                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13728                element == EL_SP_GRAVITY_ON_PORT_UP ||
13729                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13730         player->gravity = TRUE;
13731       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13732                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13733                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13734                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13735         player->gravity = FALSE;
13736     }
13737
13738     // automatically move to the next field with double speed
13739     player->programmed_action = move_direction;
13740
13741     if (player->move_delay_reset_counter == 0)
13742     {
13743       player->move_delay_reset_counter = 2;     // two double speed steps
13744
13745       DOUBLE_PLAYER_SPEED(player);
13746     }
13747
13748     PlayLevelSoundAction(x, y, ACTION_PASSING);
13749   }
13750   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13751   {
13752     RemoveField(x, y);
13753
13754     if (mode != DF_SNAP)
13755     {
13756       GfxElement[x][y] = GFX_ELEMENT(element);
13757       player->is_digging = TRUE;
13758     }
13759
13760     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13761
13762     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13763                                         player->index_bit, dig_side);
13764
13765     if (mode == DF_SNAP)
13766     {
13767       if (level.block_snap_field)
13768         setFieldForSnapping(x, y, element, move_direction);
13769       else
13770         TestIfElementTouchesCustomElement(x, y);        // for empty space
13771
13772       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13773                                           player->index_bit, dig_side);
13774     }
13775   }
13776   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13777   {
13778     RemoveField(x, y);
13779
13780     if (is_player && mode != DF_SNAP)
13781     {
13782       GfxElement[x][y] = element;
13783       player->is_collecting = TRUE;
13784     }
13785
13786     if (element == EL_SPEED_PILL)
13787     {
13788       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13789     }
13790     else if (element == EL_EXTRA_TIME && level.time > 0)
13791     {
13792       TimeLeft += level.extra_time;
13793
13794       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13795
13796       DisplayGameControlValues();
13797     }
13798     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13799     {
13800       player->shield_normal_time_left += level.shield_normal_time;
13801       if (element == EL_SHIELD_DEADLY)
13802         player->shield_deadly_time_left += level.shield_deadly_time;
13803     }
13804     else if (element == EL_DYNAMITE ||
13805              element == EL_EM_DYNAMITE ||
13806              element == EL_SP_DISK_RED)
13807     {
13808       if (player->inventory_size < MAX_INVENTORY_SIZE)
13809         player->inventory_element[player->inventory_size++] = element;
13810
13811       DrawGameDoorValues();
13812     }
13813     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13814     {
13815       player->dynabomb_count++;
13816       player->dynabombs_left++;
13817     }
13818     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13819     {
13820       player->dynabomb_size++;
13821     }
13822     else if (element == EL_DYNABOMB_INCREASE_POWER)
13823     {
13824       player->dynabomb_xl = TRUE;
13825     }
13826     else if (IS_KEY(element))
13827     {
13828       player->key[KEY_NR(element)] = TRUE;
13829
13830       DrawGameDoorValues();
13831     }
13832     else if (element == EL_DC_KEY_WHITE)
13833     {
13834       player->num_white_keys++;
13835
13836       // display white keys?
13837       // DrawGameDoorValues();
13838     }
13839     else if (IS_ENVELOPE(element))
13840     {
13841       player->show_envelope = element;
13842     }
13843     else if (element == EL_EMC_LENSES)
13844     {
13845       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13846
13847       RedrawAllInvisibleElementsForLenses();
13848     }
13849     else if (element == EL_EMC_MAGNIFIER)
13850     {
13851       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13852
13853       RedrawAllInvisibleElementsForMagnifier();
13854     }
13855     else if (IS_DROPPABLE(element) ||
13856              IS_THROWABLE(element))     // can be collected and dropped
13857     {
13858       int i;
13859
13860       if (collect_count == 0)
13861         player->inventory_infinite_element = element;
13862       else
13863         for (i = 0; i < collect_count; i++)
13864           if (player->inventory_size < MAX_INVENTORY_SIZE)
13865             player->inventory_element[player->inventory_size++] = element;
13866
13867       DrawGameDoorValues();
13868     }
13869     else if (collect_count > 0)
13870     {
13871       game.gems_still_needed -= collect_count;
13872       if (game.gems_still_needed < 0)
13873         game.gems_still_needed = 0;
13874
13875       game.snapshot.collected_item = TRUE;
13876
13877       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13878
13879       DisplayGameControlValues();
13880     }
13881
13882     RaiseScoreElement(element);
13883     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13884
13885     if (is_player)
13886       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13887                                           player->index_bit, dig_side);
13888
13889     if (mode == DF_SNAP)
13890     {
13891       if (level.block_snap_field)
13892         setFieldForSnapping(x, y, element, move_direction);
13893       else
13894         TestIfElementTouchesCustomElement(x, y);        // for empty space
13895
13896       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13897                                           player->index_bit, dig_side);
13898     }
13899   }
13900   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13901   {
13902     if (mode == DF_SNAP && element != EL_BD_ROCK)
13903       return MP_NO_ACTION;
13904
13905     if (CAN_FALL(element) && dy)
13906       return MP_NO_ACTION;
13907
13908     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13909         !(element == EL_SPRING && level.use_spring_bug))
13910       return MP_NO_ACTION;
13911
13912     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13913         ((move_direction & MV_VERTICAL &&
13914           ((element_info[element].move_pattern & MV_LEFT &&
13915             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13916            (element_info[element].move_pattern & MV_RIGHT &&
13917             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13918          (move_direction & MV_HORIZONTAL &&
13919           ((element_info[element].move_pattern & MV_UP &&
13920             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13921            (element_info[element].move_pattern & MV_DOWN &&
13922             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13923       return MP_NO_ACTION;
13924
13925     // do not push elements already moving away faster than player
13926     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13927         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13928       return MP_NO_ACTION;
13929
13930     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13931     {
13932       if (player->push_delay_value == -1 || !player_was_pushing)
13933         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13934     }
13935     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13936     {
13937       if (player->push_delay_value == -1)
13938         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13939     }
13940     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13941     {
13942       if (!player->is_pushing)
13943         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13944     }
13945
13946     player->is_pushing = TRUE;
13947     player->is_active = TRUE;
13948
13949     if (!(IN_LEV_FIELD(nextx, nexty) &&
13950           (IS_FREE(nextx, nexty) ||
13951            (IS_SB_ELEMENT(element) &&
13952             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13953            (IS_CUSTOM_ELEMENT(element) &&
13954             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13955       return MP_NO_ACTION;
13956
13957     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13958       return MP_NO_ACTION;
13959
13960     if (player->push_delay == -1)       // new pushing; restart delay
13961       player->push_delay = 0;
13962
13963     if (player->push_delay < player->push_delay_value &&
13964         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13965         element != EL_SPRING && element != EL_BALLOON)
13966     {
13967       // make sure that there is no move delay before next try to push
13968       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13969         player->move_delay = 0;
13970
13971       return MP_NO_ACTION;
13972     }
13973
13974     if (IS_CUSTOM_ELEMENT(element) &&
13975         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13976     {
13977       if (!DigFieldByCE(nextx, nexty, element))
13978         return MP_NO_ACTION;
13979     }
13980
13981     if (IS_SB_ELEMENT(element))
13982     {
13983       boolean sokoban_task_solved = FALSE;
13984
13985       if (element == EL_SOKOBAN_FIELD_FULL)
13986       {
13987         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13988
13989         IncrementSokobanFieldsNeeded();
13990         IncrementSokobanObjectsNeeded();
13991       }
13992
13993       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13994       {
13995         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13996
13997         DecrementSokobanFieldsNeeded();
13998         DecrementSokobanObjectsNeeded();
13999
14000         // sokoban object was pushed from empty field to sokoban field
14001         if (Back[x][y] == EL_EMPTY)
14002           sokoban_task_solved = TRUE;
14003       }
14004
14005       Feld[x][y] = EL_SOKOBAN_OBJECT;
14006
14007       if (Back[x][y] == Back[nextx][nexty])
14008         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14009       else if (Back[x][y] != 0)
14010         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14011                                     ACTION_EMPTYING);
14012       else
14013         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14014                                     ACTION_FILLING);
14015
14016       if (sokoban_task_solved &&
14017           game.sokoban_fields_still_needed == 0 &&
14018           game.sokoban_objects_still_needed == 0 &&
14019           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14020       {
14021         game.players_still_needed = 0;
14022
14023         LevelSolved();
14024
14025         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14026       }
14027     }
14028     else
14029       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14030
14031     InitMovingField(x, y, move_direction);
14032     GfxAction[x][y] = ACTION_PUSHING;
14033
14034     if (mode == DF_SNAP)
14035       ContinueMoving(x, y);
14036     else
14037       MovPos[x][y] = (dx != 0 ? dx : dy);
14038
14039     Pushed[x][y] = TRUE;
14040     Pushed[nextx][nexty] = TRUE;
14041
14042     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14043       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14044     else
14045       player->push_delay_value = -1;    // get new value later
14046
14047     // check for element change _after_ element has been pushed
14048     if (game.use_change_when_pushing_bug)
14049     {
14050       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14051                                  player->index_bit, dig_side);
14052       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14053                                           player->index_bit, dig_side);
14054     }
14055   }
14056   else if (IS_SWITCHABLE(element))
14057   {
14058     if (PLAYER_SWITCHING(player, x, y))
14059     {
14060       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14061                                           player->index_bit, dig_side);
14062
14063       return MP_ACTION;
14064     }
14065
14066     player->is_switching = TRUE;
14067     player->switch_x = x;
14068     player->switch_y = y;
14069
14070     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14071
14072     if (element == EL_ROBOT_WHEEL)
14073     {
14074       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14075       ZX = x;
14076       ZY = y;
14077
14078       game.robot_wheel_active = TRUE;
14079
14080       TEST_DrawLevelField(x, y);
14081     }
14082     else if (element == EL_SP_TERMINAL)
14083     {
14084       int xx, yy;
14085
14086       SCAN_PLAYFIELD(xx, yy)
14087       {
14088         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14089         {
14090           Bang(xx, yy);
14091         }
14092         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14093         {
14094           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14095
14096           ResetGfxAnimation(xx, yy);
14097           TEST_DrawLevelField(xx, yy);
14098         }
14099       }
14100     }
14101     else if (IS_BELT_SWITCH(element))
14102     {
14103       ToggleBeltSwitch(x, y);
14104     }
14105     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14106              element == EL_SWITCHGATE_SWITCH_DOWN ||
14107              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14108              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14109     {
14110       ToggleSwitchgateSwitch(x, y);
14111     }
14112     else if (element == EL_LIGHT_SWITCH ||
14113              element == EL_LIGHT_SWITCH_ACTIVE)
14114     {
14115       ToggleLightSwitch(x, y);
14116     }
14117     else if (element == EL_TIMEGATE_SWITCH ||
14118              element == EL_DC_TIMEGATE_SWITCH)
14119     {
14120       ActivateTimegateSwitch(x, y);
14121     }
14122     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14123              element == EL_BALLOON_SWITCH_RIGHT ||
14124              element == EL_BALLOON_SWITCH_UP    ||
14125              element == EL_BALLOON_SWITCH_DOWN  ||
14126              element == EL_BALLOON_SWITCH_NONE  ||
14127              element == EL_BALLOON_SWITCH_ANY)
14128     {
14129       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14130                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14131                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14132                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14133                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14134                              move_direction);
14135     }
14136     else if (element == EL_LAMP)
14137     {
14138       Feld[x][y] = EL_LAMP_ACTIVE;
14139       game.lights_still_needed--;
14140
14141       ResetGfxAnimation(x, y);
14142       TEST_DrawLevelField(x, y);
14143     }
14144     else if (element == EL_TIME_ORB_FULL)
14145     {
14146       Feld[x][y] = EL_TIME_ORB_EMPTY;
14147
14148       if (level.time > 0 || level.use_time_orb_bug)
14149       {
14150         TimeLeft += level.time_orb_time;
14151         game.no_time_limit = FALSE;
14152
14153         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14154
14155         DisplayGameControlValues();
14156       }
14157
14158       ResetGfxAnimation(x, y);
14159       TEST_DrawLevelField(x, y);
14160     }
14161     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14162              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14163     {
14164       int xx, yy;
14165
14166       game.ball_state = !game.ball_state;
14167
14168       SCAN_PLAYFIELD(xx, yy)
14169       {
14170         int e = Feld[xx][yy];
14171
14172         if (game.ball_state)
14173         {
14174           if (e == EL_EMC_MAGIC_BALL)
14175             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14176           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14177             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14178         }
14179         else
14180         {
14181           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14182             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14183           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14184             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14185         }
14186       }
14187     }
14188
14189     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14190                                         player->index_bit, dig_side);
14191
14192     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14193                                         player->index_bit, dig_side);
14194
14195     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14196                                         player->index_bit, dig_side);
14197
14198     return MP_ACTION;
14199   }
14200   else
14201   {
14202     if (!PLAYER_SWITCHING(player, x, y))
14203     {
14204       player->is_switching = TRUE;
14205       player->switch_x = x;
14206       player->switch_y = y;
14207
14208       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14209                                  player->index_bit, dig_side);
14210       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14211                                           player->index_bit, dig_side);
14212
14213       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14214                                  player->index_bit, dig_side);
14215       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14216                                           player->index_bit, dig_side);
14217     }
14218
14219     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14220                                player->index_bit, dig_side);
14221     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14222                                         player->index_bit, dig_side);
14223
14224     return MP_NO_ACTION;
14225   }
14226
14227   player->push_delay = -1;
14228
14229   if (is_player)                // function can also be called by EL_PENGUIN
14230   {
14231     if (Feld[x][y] != element)          // really digged/collected something
14232     {
14233       player->is_collecting = !player->is_digging;
14234       player->is_active = TRUE;
14235     }
14236   }
14237
14238   return MP_MOVING;
14239 }
14240
14241 static boolean DigFieldByCE(int x, int y, int digging_element)
14242 {
14243   int element = Feld[x][y];
14244
14245   if (!IS_FREE(x, y))
14246   {
14247     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14248                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14249                   ACTION_BREAKING);
14250
14251     // no element can dig solid indestructible elements
14252     if (IS_INDESTRUCTIBLE(element) &&
14253         !IS_DIGGABLE(element) &&
14254         !IS_COLLECTIBLE(element))
14255       return FALSE;
14256
14257     if (AmoebaNr[x][y] &&
14258         (element == EL_AMOEBA_FULL ||
14259          element == EL_BD_AMOEBA ||
14260          element == EL_AMOEBA_GROWING))
14261     {
14262       AmoebaCnt[AmoebaNr[x][y]]--;
14263       AmoebaCnt2[AmoebaNr[x][y]]--;
14264     }
14265
14266     if (IS_MOVING(x, y))
14267       RemoveMovingField(x, y);
14268     else
14269     {
14270       RemoveField(x, y);
14271       TEST_DrawLevelField(x, y);
14272     }
14273
14274     // if digged element was about to explode, prevent the explosion
14275     ExplodeField[x][y] = EX_TYPE_NONE;
14276
14277     PlayLevelSoundAction(x, y, action);
14278   }
14279
14280   Store[x][y] = EL_EMPTY;
14281
14282   // this makes it possible to leave the removed element again
14283   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14284     Store[x][y] = element;
14285
14286   return TRUE;
14287 }
14288
14289 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14290 {
14291   int jx = player->jx, jy = player->jy;
14292   int x = jx + dx, y = jy + dy;
14293   int snap_direction = (dx == -1 ? MV_LEFT  :
14294                         dx == +1 ? MV_RIGHT :
14295                         dy == -1 ? MV_UP    :
14296                         dy == +1 ? MV_DOWN  : MV_NONE);
14297   boolean can_continue_snapping = (level.continuous_snapping &&
14298                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14299
14300   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14301     return FALSE;
14302
14303   if (!player->active || !IN_LEV_FIELD(x, y))
14304     return FALSE;
14305
14306   if (dx && dy)
14307     return FALSE;
14308
14309   if (!dx && !dy)
14310   {
14311     if (player->MovPos == 0)
14312       player->is_pushing = FALSE;
14313
14314     player->is_snapping = FALSE;
14315
14316     if (player->MovPos == 0)
14317     {
14318       player->is_moving = FALSE;
14319       player->is_digging = FALSE;
14320       player->is_collecting = FALSE;
14321     }
14322
14323     return FALSE;
14324   }
14325
14326   // prevent snapping with already pressed snap key when not allowed
14327   if (player->is_snapping && !can_continue_snapping)
14328     return FALSE;
14329
14330   player->MovDir = snap_direction;
14331
14332   if (player->MovPos == 0)
14333   {
14334     player->is_moving = FALSE;
14335     player->is_digging = FALSE;
14336     player->is_collecting = FALSE;
14337   }
14338
14339   player->is_dropping = FALSE;
14340   player->is_dropping_pressed = FALSE;
14341   player->drop_pressed_delay = 0;
14342
14343   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14344     return FALSE;
14345
14346   player->is_snapping = TRUE;
14347   player->is_active = TRUE;
14348
14349   if (player->MovPos == 0)
14350   {
14351     player->is_moving = FALSE;
14352     player->is_digging = FALSE;
14353     player->is_collecting = FALSE;
14354   }
14355
14356   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14357     TEST_DrawLevelField(player->last_jx, player->last_jy);
14358
14359   TEST_DrawLevelField(x, y);
14360
14361   return TRUE;
14362 }
14363
14364 static boolean DropElement(struct PlayerInfo *player)
14365 {
14366   int old_element, new_element;
14367   int dropx = player->jx, dropy = player->jy;
14368   int drop_direction = player->MovDir;
14369   int drop_side = drop_direction;
14370   int drop_element = get_next_dropped_element(player);
14371
14372   /* do not drop an element on top of another element; when holding drop key
14373      pressed without moving, dropped element must move away before the next
14374      element can be dropped (this is especially important if the next element
14375      is dynamite, which can be placed on background for historical reasons) */
14376   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14377     return MP_ACTION;
14378
14379   if (IS_THROWABLE(drop_element))
14380   {
14381     dropx += GET_DX_FROM_DIR(drop_direction);
14382     dropy += GET_DY_FROM_DIR(drop_direction);
14383
14384     if (!IN_LEV_FIELD(dropx, dropy))
14385       return FALSE;
14386   }
14387
14388   old_element = Feld[dropx][dropy];     // old element at dropping position
14389   new_element = drop_element;           // default: no change when dropping
14390
14391   // check if player is active, not moving and ready to drop
14392   if (!player->active || player->MovPos || player->drop_delay > 0)
14393     return FALSE;
14394
14395   // check if player has anything that can be dropped
14396   if (new_element == EL_UNDEFINED)
14397     return FALSE;
14398
14399   // only set if player has anything that can be dropped
14400   player->is_dropping_pressed = TRUE;
14401
14402   // check if drop key was pressed long enough for EM style dynamite
14403   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14404     return FALSE;
14405
14406   // check if anything can be dropped at the current position
14407   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14408     return FALSE;
14409
14410   // collected custom elements can only be dropped on empty fields
14411   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14412     return FALSE;
14413
14414   if (old_element != EL_EMPTY)
14415     Back[dropx][dropy] = old_element;   // store old element on this field
14416
14417   ResetGfxAnimation(dropx, dropy);
14418   ResetRandomAnimationValue(dropx, dropy);
14419
14420   if (player->inventory_size > 0 ||
14421       player->inventory_infinite_element != EL_UNDEFINED)
14422   {
14423     if (player->inventory_size > 0)
14424     {
14425       player->inventory_size--;
14426
14427       DrawGameDoorValues();
14428
14429       if (new_element == EL_DYNAMITE)
14430         new_element = EL_DYNAMITE_ACTIVE;
14431       else if (new_element == EL_EM_DYNAMITE)
14432         new_element = EL_EM_DYNAMITE_ACTIVE;
14433       else if (new_element == EL_SP_DISK_RED)
14434         new_element = EL_SP_DISK_RED_ACTIVE;
14435     }
14436
14437     Feld[dropx][dropy] = new_element;
14438
14439     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14440       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14441                           el2img(Feld[dropx][dropy]), 0);
14442
14443     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14444
14445     // needed if previous element just changed to "empty" in the last frame
14446     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14447
14448     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14449                                player->index_bit, drop_side);
14450     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14451                                         CE_PLAYER_DROPS_X,
14452                                         player->index_bit, drop_side);
14453
14454     TestIfElementTouchesCustomElement(dropx, dropy);
14455   }
14456   else          // player is dropping a dyna bomb
14457   {
14458     player->dynabombs_left--;
14459
14460     Feld[dropx][dropy] = new_element;
14461
14462     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14463       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14464                           el2img(Feld[dropx][dropy]), 0);
14465
14466     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14467   }
14468
14469   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14470     InitField_WithBug1(dropx, dropy, FALSE);
14471
14472   new_element = Feld[dropx][dropy];     // element might have changed
14473
14474   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14475       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14476   {
14477     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14478       MovDir[dropx][dropy] = drop_direction;
14479
14480     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14481
14482     // do not cause impact style collision by dropping elements that can fall
14483     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14484   }
14485
14486   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14487   player->is_dropping = TRUE;
14488
14489   player->drop_pressed_delay = 0;
14490   player->is_dropping_pressed = FALSE;
14491
14492   player->drop_x = dropx;
14493   player->drop_y = dropy;
14494
14495   return TRUE;
14496 }
14497
14498 // ----------------------------------------------------------------------------
14499 // game sound playing functions
14500 // ----------------------------------------------------------------------------
14501
14502 static int *loop_sound_frame = NULL;
14503 static int *loop_sound_volume = NULL;
14504
14505 void InitPlayLevelSound(void)
14506 {
14507   int num_sounds = getSoundListSize();
14508
14509   checked_free(loop_sound_frame);
14510   checked_free(loop_sound_volume);
14511
14512   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14513   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14514 }
14515
14516 static void PlayLevelSound(int x, int y, int nr)
14517 {
14518   int sx = SCREENX(x), sy = SCREENY(y);
14519   int volume, stereo_position;
14520   int max_distance = 8;
14521   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14522
14523   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14524       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14525     return;
14526
14527   if (!IN_LEV_FIELD(x, y) ||
14528       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14529       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14530     return;
14531
14532   volume = SOUND_MAX_VOLUME;
14533
14534   if (!IN_SCR_FIELD(sx, sy))
14535   {
14536     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14537     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14538
14539     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14540   }
14541
14542   stereo_position = (SOUND_MAX_LEFT +
14543                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14544                      (SCR_FIELDX + 2 * max_distance));
14545
14546   if (IS_LOOP_SOUND(nr))
14547   {
14548     /* This assures that quieter loop sounds do not overwrite louder ones,
14549        while restarting sound volume comparison with each new game frame. */
14550
14551     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14552       return;
14553
14554     loop_sound_volume[nr] = volume;
14555     loop_sound_frame[nr] = FrameCounter;
14556   }
14557
14558   PlaySoundExt(nr, volume, stereo_position, type);
14559 }
14560
14561 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14562 {
14563   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14564                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14565                  y < LEVELY(BY1) ? LEVELY(BY1) :
14566                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14567                  sound_action);
14568 }
14569
14570 static void PlayLevelSoundAction(int x, int y, int action)
14571 {
14572   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14573 }
14574
14575 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14576 {
14577   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14578
14579   if (sound_effect != SND_UNDEFINED)
14580     PlayLevelSound(x, y, sound_effect);
14581 }
14582
14583 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14584                                               int action)
14585 {
14586   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14587
14588   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14589     PlayLevelSound(x, y, sound_effect);
14590 }
14591
14592 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14593 {
14594   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14595
14596   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14597     PlayLevelSound(x, y, sound_effect);
14598 }
14599
14600 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14601 {
14602   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14603
14604   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14605     StopSound(sound_effect);
14606 }
14607
14608 static int getLevelMusicNr(void)
14609 {
14610   if (levelset.music[level_nr] != MUS_UNDEFINED)
14611     return levelset.music[level_nr];            // from config file
14612   else
14613     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14614 }
14615
14616 static void FadeLevelSounds(void)
14617 {
14618   FadeSounds();
14619 }
14620
14621 static void FadeLevelMusic(void)
14622 {
14623   int music_nr = getLevelMusicNr();
14624   char *curr_music = getCurrentlyPlayingMusicFilename();
14625   char *next_music = getMusicInfoEntryFilename(music_nr);
14626
14627   if (!strEqual(curr_music, next_music))
14628     FadeMusic();
14629 }
14630
14631 void FadeLevelSoundsAndMusic(void)
14632 {
14633   FadeLevelSounds();
14634   FadeLevelMusic();
14635 }
14636
14637 static void PlayLevelMusic(void)
14638 {
14639   int music_nr = getLevelMusicNr();
14640   char *curr_music = getCurrentlyPlayingMusicFilename();
14641   char *next_music = getMusicInfoEntryFilename(music_nr);
14642
14643   if (!strEqual(curr_music, next_music))
14644     PlayMusicLoop(music_nr);
14645 }
14646
14647 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14648 {
14649   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14650   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14651   int x = xx - 1 - offset;
14652   int y = yy - 1 - offset;
14653
14654   switch (sample)
14655   {
14656     case SAMPLE_blank:
14657       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14658       break;
14659
14660     case SAMPLE_roll:
14661       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14662       break;
14663
14664     case SAMPLE_stone:
14665       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14666       break;
14667
14668     case SAMPLE_nut:
14669       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14670       break;
14671
14672     case SAMPLE_crack:
14673       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14674       break;
14675
14676     case SAMPLE_bug:
14677       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14678       break;
14679
14680     case SAMPLE_tank:
14681       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14682       break;
14683
14684     case SAMPLE_android_clone:
14685       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14686       break;
14687
14688     case SAMPLE_android_move:
14689       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14690       break;
14691
14692     case SAMPLE_spring:
14693       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14694       break;
14695
14696     case SAMPLE_slurp:
14697       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14698       break;
14699
14700     case SAMPLE_eater:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14702       break;
14703
14704     case SAMPLE_eater_eat:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14706       break;
14707
14708     case SAMPLE_alien:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14710       break;
14711
14712     case SAMPLE_collect:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14714       break;
14715
14716     case SAMPLE_diamond:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14718       break;
14719
14720     case SAMPLE_squash:
14721       // !!! CHECK THIS !!!
14722 #if 1
14723       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14724 #else
14725       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14726 #endif
14727       break;
14728
14729     case SAMPLE_wonderfall:
14730       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14731       break;
14732
14733     case SAMPLE_drip:
14734       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14735       break;
14736
14737     case SAMPLE_push:
14738       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14739       break;
14740
14741     case SAMPLE_dirt:
14742       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14743       break;
14744
14745     case SAMPLE_acid:
14746       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14747       break;
14748
14749     case SAMPLE_ball:
14750       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14751       break;
14752
14753     case SAMPLE_grow:
14754       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14755       break;
14756
14757     case SAMPLE_wonder:
14758       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14759       break;
14760
14761     case SAMPLE_door:
14762       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14763       break;
14764
14765     case SAMPLE_exit_open:
14766       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14767       break;
14768
14769     case SAMPLE_exit_leave:
14770       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14771       break;
14772
14773     case SAMPLE_dynamite:
14774       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14775       break;
14776
14777     case SAMPLE_tick:
14778       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14779       break;
14780
14781     case SAMPLE_press:
14782       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14783       break;
14784
14785     case SAMPLE_wheel:
14786       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14787       break;
14788
14789     case SAMPLE_boom:
14790       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14791       break;
14792
14793     case SAMPLE_die:
14794       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14795       break;
14796
14797     case SAMPLE_time:
14798       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14799       break;
14800
14801     default:
14802       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14803       break;
14804   }
14805 }
14806
14807 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14808 {
14809   int element = map_element_SP_to_RND(element_sp);
14810   int action = map_action_SP_to_RND(action_sp);
14811   int offset = (setup.sp_show_border_elements ? 0 : 1);
14812   int x = xx - offset;
14813   int y = yy - offset;
14814
14815   PlayLevelSoundElementAction(x, y, element, action);
14816 }
14817
14818 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14819 {
14820   int element = map_element_MM_to_RND(element_mm);
14821   int action = map_action_MM_to_RND(action_mm);
14822   int offset = 0;
14823   int x = xx - offset;
14824   int y = yy - offset;
14825
14826   if (!IS_MM_ELEMENT(element))
14827     element = EL_MM_DEFAULT;
14828
14829   PlayLevelSoundElementAction(x, y, element, action);
14830 }
14831
14832 void PlaySound_MM(int sound_mm)
14833 {
14834   int sound = map_sound_MM_to_RND(sound_mm);
14835
14836   if (sound == SND_UNDEFINED)
14837     return;
14838
14839   PlaySound(sound);
14840 }
14841
14842 void PlaySoundLoop_MM(int sound_mm)
14843 {
14844   int sound = map_sound_MM_to_RND(sound_mm);
14845
14846   if (sound == SND_UNDEFINED)
14847     return;
14848
14849   PlaySoundLoop(sound);
14850 }
14851
14852 void StopSound_MM(int sound_mm)
14853 {
14854   int sound = map_sound_MM_to_RND(sound_mm);
14855
14856   if (sound == SND_UNDEFINED)
14857     return;
14858
14859   StopSound(sound);
14860 }
14861
14862 void RaiseScore(int value)
14863 {
14864   game.score += value;
14865
14866   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14867
14868   DisplayGameControlValues();
14869 }
14870
14871 void RaiseScoreElement(int element)
14872 {
14873   switch (element)
14874   {
14875     case EL_EMERALD:
14876     case EL_BD_DIAMOND:
14877     case EL_EMERALD_YELLOW:
14878     case EL_EMERALD_RED:
14879     case EL_EMERALD_PURPLE:
14880     case EL_SP_INFOTRON:
14881       RaiseScore(level.score[SC_EMERALD]);
14882       break;
14883     case EL_DIAMOND:
14884       RaiseScore(level.score[SC_DIAMOND]);
14885       break;
14886     case EL_CRYSTAL:
14887       RaiseScore(level.score[SC_CRYSTAL]);
14888       break;
14889     case EL_PEARL:
14890       RaiseScore(level.score[SC_PEARL]);
14891       break;
14892     case EL_BUG:
14893     case EL_BD_BUTTERFLY:
14894     case EL_SP_ELECTRON:
14895       RaiseScore(level.score[SC_BUG]);
14896       break;
14897     case EL_SPACESHIP:
14898     case EL_BD_FIREFLY:
14899     case EL_SP_SNIKSNAK:
14900       RaiseScore(level.score[SC_SPACESHIP]);
14901       break;
14902     case EL_YAMYAM:
14903     case EL_DARK_YAMYAM:
14904       RaiseScore(level.score[SC_YAMYAM]);
14905       break;
14906     case EL_ROBOT:
14907       RaiseScore(level.score[SC_ROBOT]);
14908       break;
14909     case EL_PACMAN:
14910       RaiseScore(level.score[SC_PACMAN]);
14911       break;
14912     case EL_NUT:
14913       RaiseScore(level.score[SC_NUT]);
14914       break;
14915     case EL_DYNAMITE:
14916     case EL_EM_DYNAMITE:
14917     case EL_SP_DISK_RED:
14918     case EL_DYNABOMB_INCREASE_NUMBER:
14919     case EL_DYNABOMB_INCREASE_SIZE:
14920     case EL_DYNABOMB_INCREASE_POWER:
14921       RaiseScore(level.score[SC_DYNAMITE]);
14922       break;
14923     case EL_SHIELD_NORMAL:
14924     case EL_SHIELD_DEADLY:
14925       RaiseScore(level.score[SC_SHIELD]);
14926       break;
14927     case EL_EXTRA_TIME:
14928       RaiseScore(level.extra_time_score);
14929       break;
14930     case EL_KEY_1:
14931     case EL_KEY_2:
14932     case EL_KEY_3:
14933     case EL_KEY_4:
14934     case EL_EM_KEY_1:
14935     case EL_EM_KEY_2:
14936     case EL_EM_KEY_3:
14937     case EL_EM_KEY_4:
14938     case EL_EMC_KEY_5:
14939     case EL_EMC_KEY_6:
14940     case EL_EMC_KEY_7:
14941     case EL_EMC_KEY_8:
14942     case EL_DC_KEY_WHITE:
14943       RaiseScore(level.score[SC_KEY]);
14944       break;
14945     default:
14946       RaiseScore(element_info[element].collect_score);
14947       break;
14948   }
14949 }
14950
14951 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14952 {
14953   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14954   {
14955     // closing door required in case of envelope style request dialogs
14956     if (!skip_request)
14957       CloseDoor(DOOR_CLOSE_1);
14958
14959     if (network.enabled)
14960       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14961     else
14962     {
14963       if (quick_quit)
14964         FadeSkipNextFadeIn();
14965
14966       SetGameStatus(GAME_MODE_MAIN);
14967
14968       DrawMainMenu();
14969     }
14970   }
14971   else          // continue playing the game
14972   {
14973     if (tape.playing && tape.deactivate_display)
14974       TapeDeactivateDisplayOff(TRUE);
14975
14976     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14977
14978     if (tape.playing && tape.deactivate_display)
14979       TapeDeactivateDisplayOn();
14980   }
14981 }
14982
14983 void RequestQuitGame(boolean ask_if_really_quit)
14984 {
14985   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14986   boolean skip_request = game.all_players_gone || quick_quit;
14987
14988   RequestQuitGameExt(skip_request, quick_quit,
14989                      "Do you really want to quit the game?");
14990 }
14991
14992 void RequestRestartGame(char *message)
14993 {
14994   game.restart_game_message = NULL;
14995
14996   boolean has_started_game = hasStartedNetworkGame();
14997   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
14998
14999   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15000   {
15001     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15002   }
15003   else
15004   {
15005     SetGameStatus(GAME_MODE_MAIN);
15006
15007     DrawMainMenu();
15008   }
15009 }
15010
15011 void CheckGameOver(void)
15012 {
15013   static boolean last_game_over = FALSE;
15014   static int game_over_delay = 0;
15015   int game_over_delay_value = 50;
15016   boolean game_over = checkGameFailed();
15017
15018   // do not handle game over if request dialog is already active
15019   if (game.request_active)
15020     return;
15021
15022   if (!game_over)
15023   {
15024     last_game_over = FALSE;
15025     game_over_delay = game_over_delay_value;
15026
15027     return;
15028   }
15029
15030   if (game_over_delay > 0)
15031   {
15032     game_over_delay--;
15033
15034     return;
15035   }
15036
15037   if (last_game_over != game_over)
15038     game.restart_game_message = (hasStartedNetworkGame() ?
15039                                  "Game over! Play it again?" :
15040                                  "Game over!");
15041
15042   last_game_over = game_over;
15043 }
15044
15045 boolean checkGameSolved(void)
15046 {
15047   // set for all game engines if level was solved
15048   return game.LevelSolved_GameEnd;
15049 }
15050
15051 boolean checkGameFailed(void)
15052 {
15053   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15054     return (game_em.game_over && !game_em.level_solved);
15055   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15056     return (game_sp.game_over && !game_sp.level_solved);
15057   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15058     return (game_mm.game_over && !game_mm.level_solved);
15059   else                          // GAME_ENGINE_TYPE_RND
15060     return (game.GameOver && !game.LevelSolved);
15061 }
15062
15063 boolean checkGameEnded(void)
15064 {
15065   return (checkGameSolved() || checkGameFailed());
15066 }
15067
15068
15069 // ----------------------------------------------------------------------------
15070 // random generator functions
15071 // ----------------------------------------------------------------------------
15072
15073 unsigned int InitEngineRandom_RND(int seed)
15074 {
15075   game.num_random_calls = 0;
15076
15077   return InitEngineRandom(seed);
15078 }
15079
15080 unsigned int RND(int max)
15081 {
15082   if (max > 0)
15083   {
15084     game.num_random_calls++;
15085
15086     return GetEngineRandom(max);
15087   }
15088
15089   return 0;
15090 }
15091
15092
15093 // ----------------------------------------------------------------------------
15094 // game engine snapshot handling functions
15095 // ----------------------------------------------------------------------------
15096
15097 struct EngineSnapshotInfo
15098 {
15099   // runtime values for custom element collect score
15100   int collect_score[NUM_CUSTOM_ELEMENTS];
15101
15102   // runtime values for group element choice position
15103   int choice_pos[NUM_GROUP_ELEMENTS];
15104
15105   // runtime values for belt position animations
15106   int belt_graphic[4][NUM_BELT_PARTS];
15107   int belt_anim_mode[4][NUM_BELT_PARTS];
15108 };
15109
15110 static struct EngineSnapshotInfo engine_snapshot_rnd;
15111 static char *snapshot_level_identifier = NULL;
15112 static int snapshot_level_nr = -1;
15113
15114 static void SaveEngineSnapshotValues_RND(void)
15115 {
15116   static int belt_base_active_element[4] =
15117   {
15118     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15119     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15120     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15121     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15122   };
15123   int i, j;
15124
15125   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15126   {
15127     int element = EL_CUSTOM_START + i;
15128
15129     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15130   }
15131
15132   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15133   {
15134     int element = EL_GROUP_START + i;
15135
15136     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15137   }
15138
15139   for (i = 0; i < 4; i++)
15140   {
15141     for (j = 0; j < NUM_BELT_PARTS; j++)
15142     {
15143       int element = belt_base_active_element[i] + j;
15144       int graphic = el2img(element);
15145       int anim_mode = graphic_info[graphic].anim_mode;
15146
15147       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15148       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15149     }
15150   }
15151 }
15152
15153 static void LoadEngineSnapshotValues_RND(void)
15154 {
15155   unsigned int num_random_calls = game.num_random_calls;
15156   int i, j;
15157
15158   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15159   {
15160     int element = EL_CUSTOM_START + i;
15161
15162     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15163   }
15164
15165   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15166   {
15167     int element = EL_GROUP_START + i;
15168
15169     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15170   }
15171
15172   for (i = 0; i < 4; i++)
15173   {
15174     for (j = 0; j < NUM_BELT_PARTS; j++)
15175     {
15176       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15177       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15178
15179       graphic_info[graphic].anim_mode = anim_mode;
15180     }
15181   }
15182
15183   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15184   {
15185     InitRND(tape.random_seed);
15186     for (i = 0; i < num_random_calls; i++)
15187       RND(1);
15188   }
15189
15190   if (game.num_random_calls != num_random_calls)
15191   {
15192     Error(ERR_INFO, "number of random calls out of sync");
15193     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15194     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15195     Error(ERR_EXIT, "this should not happen -- please debug");
15196   }
15197 }
15198
15199 void FreeEngineSnapshotSingle(void)
15200 {
15201   FreeSnapshotSingle();
15202
15203   setString(&snapshot_level_identifier, NULL);
15204   snapshot_level_nr = -1;
15205 }
15206
15207 void FreeEngineSnapshotList(void)
15208 {
15209   FreeSnapshotList();
15210 }
15211
15212 static ListNode *SaveEngineSnapshotBuffers(void)
15213 {
15214   ListNode *buffers = NULL;
15215
15216   // copy some special values to a structure better suited for the snapshot
15217
15218   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15219     SaveEngineSnapshotValues_RND();
15220   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15221     SaveEngineSnapshotValues_EM();
15222   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15223     SaveEngineSnapshotValues_SP(&buffers);
15224   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15225     SaveEngineSnapshotValues_MM(&buffers);
15226
15227   // save values stored in special snapshot structure
15228
15229   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15230     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15231   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15232     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15233   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15234     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15235   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15236     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15237
15238   // save further RND engine values
15239
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15241   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15242   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15243
15244   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15246
15247   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15248   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15249   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15250   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15251   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15252
15253   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15254   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15255   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15256
15257   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15258
15259   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15260   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15261
15262   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15263   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15264   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15265   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15266   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15267   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15268   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15269   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15270   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15271   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15272   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15273   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15274   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15275   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15276   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15277   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15278   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15279   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15280
15281   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15282   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15283
15284   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15285   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15286   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15287
15288   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15289   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15290
15291   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15293   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15294   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15295   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15296
15297   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15298   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15299
15300 #if 0
15301   ListNode *node = engine_snapshot_list_rnd;
15302   int num_bytes = 0;
15303
15304   while (node != NULL)
15305   {
15306     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15307
15308     node = node->next;
15309   }
15310
15311   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15312 #endif
15313
15314   return buffers;
15315 }
15316
15317 void SaveEngineSnapshotSingle(void)
15318 {
15319   ListNode *buffers = SaveEngineSnapshotBuffers();
15320
15321   // finally save all snapshot buffers to single snapshot
15322   SaveSnapshotSingle(buffers);
15323
15324   // save level identification information
15325   setString(&snapshot_level_identifier, leveldir_current->identifier);
15326   snapshot_level_nr = level_nr;
15327 }
15328
15329 boolean CheckSaveEngineSnapshotToList(void)
15330 {
15331   boolean save_snapshot =
15332     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15333      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15334       game.snapshot.changed_action) ||
15335      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15336       game.snapshot.collected_item));
15337
15338   game.snapshot.changed_action = FALSE;
15339   game.snapshot.collected_item = FALSE;
15340   game.snapshot.save_snapshot = save_snapshot;
15341
15342   return save_snapshot;
15343 }
15344
15345 void SaveEngineSnapshotToList(void)
15346 {
15347   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15348       tape.quick_resume)
15349     return;
15350
15351   ListNode *buffers = SaveEngineSnapshotBuffers();
15352
15353   // finally save all snapshot buffers to snapshot list
15354   SaveSnapshotToList(buffers);
15355 }
15356
15357 void SaveEngineSnapshotToListInitial(void)
15358 {
15359   FreeEngineSnapshotList();
15360
15361   SaveEngineSnapshotToList();
15362 }
15363
15364 static void LoadEngineSnapshotValues(void)
15365 {
15366   // restore special values from snapshot structure
15367
15368   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15369     LoadEngineSnapshotValues_RND();
15370   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15371     LoadEngineSnapshotValues_EM();
15372   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15373     LoadEngineSnapshotValues_SP();
15374   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15375     LoadEngineSnapshotValues_MM();
15376 }
15377
15378 void LoadEngineSnapshotSingle(void)
15379 {
15380   LoadSnapshotSingle();
15381
15382   LoadEngineSnapshotValues();
15383 }
15384
15385 static void LoadEngineSnapshot_Undo(int steps)
15386 {
15387   LoadSnapshotFromList_Older(steps);
15388
15389   LoadEngineSnapshotValues();
15390 }
15391
15392 static void LoadEngineSnapshot_Redo(int steps)
15393 {
15394   LoadSnapshotFromList_Newer(steps);
15395
15396   LoadEngineSnapshotValues();
15397 }
15398
15399 boolean CheckEngineSnapshotSingle(void)
15400 {
15401   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15402           snapshot_level_nr == level_nr);
15403 }
15404
15405 boolean CheckEngineSnapshotList(void)
15406 {
15407   return CheckSnapshotList();
15408 }
15409
15410
15411 // ---------- new game button stuff -------------------------------------------
15412
15413 static struct
15414 {
15415   int graphic;
15416   struct XY *pos;
15417   int gadget_id;
15418   boolean *setup_value;
15419   boolean allowed_on_tape;
15420   char *infotext;
15421 } gamebutton_info[NUM_GAME_BUTTONS] =
15422 {
15423   {
15424     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15425     GAME_CTRL_ID_STOP,                          NULL,
15426     TRUE,                                       "stop game"
15427   },
15428   {
15429     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15430     GAME_CTRL_ID_PAUSE,                         NULL,
15431     TRUE,                                       "pause game"
15432   },
15433   {
15434     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15435     GAME_CTRL_ID_PLAY,                          NULL,
15436     TRUE,                                       "play game"
15437   },
15438   {
15439     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15440     GAME_CTRL_ID_UNDO,                          NULL,
15441     TRUE,                                       "undo step"
15442   },
15443   {
15444     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15445     GAME_CTRL_ID_REDO,                          NULL,
15446     TRUE,                                       "redo step"
15447   },
15448   {
15449     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15450     GAME_CTRL_ID_SAVE,                          NULL,
15451     TRUE,                                       "save game"
15452   },
15453   {
15454     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15455     GAME_CTRL_ID_PAUSE2,                        NULL,
15456     TRUE,                                       "pause game"
15457   },
15458   {
15459     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15460     GAME_CTRL_ID_LOAD,                          NULL,
15461     TRUE,                                       "load game"
15462   },
15463   {
15464     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15465     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15466     FALSE,                                      "stop game"
15467   },
15468   {
15469     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15470     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15471     FALSE,                                      "pause game"
15472   },
15473   {
15474     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15475     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15476     FALSE,                                      "play game"
15477   },
15478   {
15479     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15480     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15481     TRUE,                                       "background music on/off"
15482   },
15483   {
15484     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15485     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15486     TRUE,                                       "sound loops on/off"
15487   },
15488   {
15489     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15490     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15491     TRUE,                                       "normal sounds on/off"
15492   },
15493   {
15494     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15495     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15496     FALSE,                                      "background music on/off"
15497   },
15498   {
15499     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15500     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15501     FALSE,                                      "sound loops on/off"
15502   },
15503   {
15504     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15505     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15506     FALSE,                                      "normal sounds on/off"
15507   }
15508 };
15509
15510 void CreateGameButtons(void)
15511 {
15512   int i;
15513
15514   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15515   {
15516     int graphic = gamebutton_info[i].graphic;
15517     struct GraphicInfo *gfx = &graphic_info[graphic];
15518     struct XY *pos = gamebutton_info[i].pos;
15519     struct GadgetInfo *gi;
15520     int button_type;
15521     boolean checked;
15522     unsigned int event_mask;
15523     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15524     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15525     int base_x = (on_tape ? VX : DX);
15526     int base_y = (on_tape ? VY : DY);
15527     int gd_x   = gfx->src_x;
15528     int gd_y   = gfx->src_y;
15529     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15530     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15531     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15532     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15533     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15534     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15535     int id = i;
15536
15537     if (gfx->bitmap == NULL)
15538     {
15539       game_gadget[id] = NULL;
15540
15541       continue;
15542     }
15543
15544     if (id == GAME_CTRL_ID_STOP ||
15545         id == GAME_CTRL_ID_PANEL_STOP ||
15546         id == GAME_CTRL_ID_PLAY ||
15547         id == GAME_CTRL_ID_PANEL_PLAY ||
15548         id == GAME_CTRL_ID_SAVE ||
15549         id == GAME_CTRL_ID_LOAD)
15550     {
15551       button_type = GD_TYPE_NORMAL_BUTTON;
15552       checked = FALSE;
15553       event_mask = GD_EVENT_RELEASED;
15554     }
15555     else if (id == GAME_CTRL_ID_UNDO ||
15556              id == GAME_CTRL_ID_REDO)
15557     {
15558       button_type = GD_TYPE_NORMAL_BUTTON;
15559       checked = FALSE;
15560       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15561     }
15562     else
15563     {
15564       button_type = GD_TYPE_CHECK_BUTTON;
15565       checked = (gamebutton_info[i].setup_value != NULL ?
15566                  *gamebutton_info[i].setup_value : FALSE);
15567       event_mask = GD_EVENT_PRESSED;
15568     }
15569
15570     gi = CreateGadget(GDI_CUSTOM_ID, id,
15571                       GDI_IMAGE_ID, graphic,
15572                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15573                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15574                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15575                       GDI_WIDTH, gfx->width,
15576                       GDI_HEIGHT, gfx->height,
15577                       GDI_TYPE, button_type,
15578                       GDI_STATE, GD_BUTTON_UNPRESSED,
15579                       GDI_CHECKED, checked,
15580                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15581                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15582                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15583                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15584                       GDI_DIRECT_DRAW, FALSE,
15585                       GDI_EVENT_MASK, event_mask,
15586                       GDI_CALLBACK_ACTION, HandleGameButtons,
15587                       GDI_END);
15588
15589     if (gi == NULL)
15590       Error(ERR_EXIT, "cannot create gadget");
15591
15592     game_gadget[id] = gi;
15593   }
15594 }
15595
15596 void FreeGameButtons(void)
15597 {
15598   int i;
15599
15600   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15601     FreeGadget(game_gadget[i]);
15602 }
15603
15604 static void UnmapGameButtonsAtSamePosition(int id)
15605 {
15606   int i;
15607
15608   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15609     if (i != id &&
15610         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15611         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15612       UnmapGadget(game_gadget[i]);
15613 }
15614
15615 static void UnmapGameButtonsAtSamePosition_All(void)
15616 {
15617   if (setup.show_snapshot_buttons)
15618   {
15619     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15620     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15621     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15622   }
15623   else
15624   {
15625     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15626     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15627     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15628
15629     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15630     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15631     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15632   }
15633 }
15634
15635 static void MapGameButtonsAtSamePosition(int id)
15636 {
15637   int i;
15638
15639   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15640     if (i != id &&
15641         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15642         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15643       MapGadget(game_gadget[i]);
15644
15645   UnmapGameButtonsAtSamePosition_All();
15646 }
15647
15648 void MapUndoRedoButtons(void)
15649 {
15650   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15651   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15652
15653   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15654   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15655
15656   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15657 }
15658
15659 void UnmapUndoRedoButtons(void)
15660 {
15661   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15662   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15663
15664   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15665   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15666
15667   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15668 }
15669
15670 static void MapGameButtonsExt(boolean on_tape)
15671 {
15672   int i;
15673
15674   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15675     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15676         i != GAME_CTRL_ID_UNDO &&
15677         i != GAME_CTRL_ID_REDO)
15678       MapGadget(game_gadget[i]);
15679
15680   UnmapGameButtonsAtSamePosition_All();
15681
15682   RedrawGameButtons();
15683 }
15684
15685 static void UnmapGameButtonsExt(boolean on_tape)
15686 {
15687   int i;
15688
15689   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15690     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15691       UnmapGadget(game_gadget[i]);
15692 }
15693
15694 static void RedrawGameButtonsExt(boolean on_tape)
15695 {
15696   int i;
15697
15698   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15699     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15700       RedrawGadget(game_gadget[i]);
15701
15702   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15703   redraw_mask &= ~REDRAW_ALL;
15704 }
15705
15706 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15707 {
15708   if (gi == NULL)
15709     return;
15710
15711   gi->checked = state;
15712 }
15713
15714 static void RedrawSoundButtonGadget(int id)
15715 {
15716   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15717              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15718              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15719              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15720              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15721              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15722              id);
15723
15724   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15725   RedrawGadget(game_gadget[id2]);
15726 }
15727
15728 void MapGameButtons(void)
15729 {
15730   MapGameButtonsExt(FALSE);
15731 }
15732
15733 void UnmapGameButtons(void)
15734 {
15735   UnmapGameButtonsExt(FALSE);
15736 }
15737
15738 void RedrawGameButtons(void)
15739 {
15740   RedrawGameButtonsExt(FALSE);
15741 }
15742
15743 void MapGameButtonsOnTape(void)
15744 {
15745   MapGameButtonsExt(TRUE);
15746 }
15747
15748 void UnmapGameButtonsOnTape(void)
15749 {
15750   UnmapGameButtonsExt(TRUE);
15751 }
15752
15753 void RedrawGameButtonsOnTape(void)
15754 {
15755   RedrawGameButtonsExt(TRUE);
15756 }
15757
15758 static void GameUndoRedoExt(void)
15759 {
15760   ClearPlayerAction();
15761
15762   tape.pausing = TRUE;
15763
15764   RedrawPlayfield();
15765   UpdateAndDisplayGameControlValues();
15766
15767   DrawCompleteVideoDisplay();
15768   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15769   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15770   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15771
15772   BackToFront();
15773 }
15774
15775 static void GameUndo(int steps)
15776 {
15777   if (!CheckEngineSnapshotList())
15778     return;
15779
15780   LoadEngineSnapshot_Undo(steps);
15781
15782   GameUndoRedoExt();
15783 }
15784
15785 static void GameRedo(int steps)
15786 {
15787   if (!CheckEngineSnapshotList())
15788     return;
15789
15790   LoadEngineSnapshot_Redo(steps);
15791
15792   GameUndoRedoExt();
15793 }
15794
15795 static void HandleGameButtonsExt(int id, int button)
15796 {
15797   static boolean game_undo_executed = FALSE;
15798   int steps = BUTTON_STEPSIZE(button);
15799   boolean handle_game_buttons =
15800     (game_status == GAME_MODE_PLAYING ||
15801      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15802
15803   if (!handle_game_buttons)
15804     return;
15805
15806   switch (id)
15807   {
15808     case GAME_CTRL_ID_STOP:
15809     case GAME_CTRL_ID_PANEL_STOP:
15810       if (game_status == GAME_MODE_MAIN)
15811         break;
15812
15813       if (tape.playing)
15814         TapeStop();
15815       else
15816         RequestQuitGame(TRUE);
15817
15818       break;
15819
15820     case GAME_CTRL_ID_PAUSE:
15821     case GAME_CTRL_ID_PAUSE2:
15822     case GAME_CTRL_ID_PANEL_PAUSE:
15823       if (network.enabled && game_status == GAME_MODE_PLAYING)
15824       {
15825         if (tape.pausing)
15826           SendToServer_ContinuePlaying();
15827         else
15828           SendToServer_PausePlaying();
15829       }
15830       else
15831         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15832
15833       game_undo_executed = FALSE;
15834
15835       break;
15836
15837     case GAME_CTRL_ID_PLAY:
15838     case GAME_CTRL_ID_PANEL_PLAY:
15839       if (game_status == GAME_MODE_MAIN)
15840       {
15841         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15842       }
15843       else if (tape.pausing)
15844       {
15845         if (network.enabled)
15846           SendToServer_ContinuePlaying();
15847         else
15848           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15849       }
15850       break;
15851
15852     case GAME_CTRL_ID_UNDO:
15853       // Important: When using "save snapshot when collecting an item" mode,
15854       // load last (current) snapshot for first "undo" after pressing "pause"
15855       // (else the last-but-one snapshot would be loaded, because the snapshot
15856       // pointer already points to the last snapshot when pressing "pause",
15857       // which is fine for "every step/move" mode, but not for "every collect")
15858       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15859           !game_undo_executed)
15860         steps--;
15861
15862       game_undo_executed = TRUE;
15863
15864       GameUndo(steps);
15865       break;
15866
15867     case GAME_CTRL_ID_REDO:
15868       GameRedo(steps);
15869       break;
15870
15871     case GAME_CTRL_ID_SAVE:
15872       TapeQuickSave();
15873       break;
15874
15875     case GAME_CTRL_ID_LOAD:
15876       TapeQuickLoad();
15877       break;
15878
15879     case SOUND_CTRL_ID_MUSIC:
15880     case SOUND_CTRL_ID_PANEL_MUSIC:
15881       if (setup.sound_music)
15882       { 
15883         setup.sound_music = FALSE;
15884
15885         FadeMusic();
15886       }
15887       else if (audio.music_available)
15888       { 
15889         setup.sound = setup.sound_music = TRUE;
15890
15891         SetAudioMode(setup.sound);
15892
15893         if (game_status == GAME_MODE_PLAYING)
15894           PlayLevelMusic();
15895       }
15896
15897       RedrawSoundButtonGadget(id);
15898
15899       break;
15900
15901     case SOUND_CTRL_ID_LOOPS:
15902     case SOUND_CTRL_ID_PANEL_LOOPS:
15903       if (setup.sound_loops)
15904         setup.sound_loops = FALSE;
15905       else if (audio.loops_available)
15906       {
15907         setup.sound = setup.sound_loops = TRUE;
15908
15909         SetAudioMode(setup.sound);
15910       }
15911
15912       RedrawSoundButtonGadget(id);
15913
15914       break;
15915
15916     case SOUND_CTRL_ID_SIMPLE:
15917     case SOUND_CTRL_ID_PANEL_SIMPLE:
15918       if (setup.sound_simple)
15919         setup.sound_simple = FALSE;
15920       else if (audio.sound_available)
15921       {
15922         setup.sound = setup.sound_simple = TRUE;
15923
15924         SetAudioMode(setup.sound);
15925       }
15926
15927       RedrawSoundButtonGadget(id);
15928
15929       break;
15930
15931     default:
15932       break;
15933   }
15934 }
15935
15936 static void HandleGameButtons(struct GadgetInfo *gi)
15937 {
15938   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15939 }
15940
15941 void HandleSoundButtonKeys(Key key)
15942 {
15943   if (key == setup.shortcut.sound_simple)
15944     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15945   else if (key == setup.shortcut.sound_loops)
15946     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15947   else if (key == setup.shortcut.sound_music)
15948     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15949 }