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