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