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