added setup option to force scroll delay when using the EM engine
[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 GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_state)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_state)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Feld[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843   // --------------------------------------------------------------------------
2844   // set flags for bugs and changes according to active game engine version
2845   // --------------------------------------------------------------------------
2846
2847   /*
2848     Summary of bugfix/change:
2849     Fixed handling for custom elements that change when pushed by the player.
2850
2851     Fixed/changed in version:
2852     3.1.0
2853
2854     Description:
2855     Before 3.1.0, custom elements that "change when pushing" changed directly
2856     after the player started pushing them (until then handled in "DigField()").
2857     Since 3.1.0, these custom elements are not changed until the "pushing"
2858     move of the element is finished (now handled in "ContinueMoving()").
2859
2860     Affected levels/tapes:
2861     The first condition is generally needed for all levels/tapes before version
2862     3.1.0, which might use the old behaviour before it was changed; known tapes
2863     that are affected are some tapes from the level set "Walpurgis Gardens" by
2864     Jamie Cullen.
2865     The second condition is an exception from the above case and is needed for
2866     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2867     above (including some development versions of 3.1.0), but before it was
2868     known that this change would break tapes like the above and was fixed in
2869     3.1.1, so that the changed behaviour was active although the engine version
2870     while recording maybe was before 3.1.0. There is at least one tape that is
2871     affected by this exception, which is the tape for the one-level set "Bug
2872     Machine" by Juergen Bonhagen.
2873   */
2874
2875   game.use_change_when_pushing_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2877      !(tape.playing &&
2878        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2879        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2880
2881   /*
2882     Summary of bugfix/change:
2883     Fixed handling for blocking the field the player leaves when moving.
2884
2885     Fixed/changed in version:
2886     3.1.1
2887
2888     Description:
2889     Before 3.1.1, when "block last field when moving" was enabled, the field
2890     the player is leaving when moving was blocked for the time of the move,
2891     and was directly unblocked afterwards. This resulted in the last field
2892     being blocked for exactly one less than the number of frames of one player
2893     move. Additionally, even when blocking was disabled, the last field was
2894     blocked for exactly one frame.
2895     Since 3.1.1, due to changes in player movement handling, the last field
2896     is not blocked at all when blocking is disabled. When blocking is enabled,
2897     the last field is blocked for exactly the number of frames of one player
2898     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2899     last field is blocked for exactly one more than the number of frames of
2900     one player move.
2901
2902     Affected levels/tapes:
2903     (!!! yet to be determined -- probably many !!!)
2904   */
2905
2906   game.use_block_last_field_bug =
2907     (game.engine_version < VERSION_IDENT(3,1,1,0));
2908
2909   /* various special flags and settings for native Emerald Mine game engine */
2910
2911   game_em.use_single_button =
2912     (game.engine_version > VERSION_IDENT(4,0,0,2));
2913
2914   game_em.use_snap_key_bug =
2915     (game.engine_version < VERSION_IDENT(4,0,1,0));
2916
2917   game_em.use_old_explosions =
2918     (game.engine_version < VERSION_IDENT(4,1,4,2));
2919
2920   // --------------------------------------------------------------------------
2921
2922   // set maximal allowed number of custom element changes per game frame
2923   game.max_num_changes_per_frame = 1;
2924
2925   // default scan direction: scan playfield from top/left to bottom/right
2926   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2927
2928   // dynamically adjust element properties according to game engine version
2929   InitElementPropertiesEngine(game.engine_version);
2930
2931 #if 0
2932   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2933   printf("          tape version == %06d [%s] [file: %06d]\n",
2934          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2935          tape.file_version);
2936   printf("       => game.engine_version == %06d\n", game.engine_version);
2937 #endif
2938
2939   // ---------- initialize player's initial move delay ------------------------
2940
2941   // dynamically adjust player properties according to level information
2942   for (i = 0; i < MAX_PLAYERS; i++)
2943     game.initial_move_delay_value[i] =
2944       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2945
2946   // dynamically adjust player properties according to game engine version
2947   for (i = 0; i < MAX_PLAYERS; i++)
2948     game.initial_move_delay[i] =
2949       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2950        game.initial_move_delay_value[i] : 0);
2951
2952   // ---------- initialize player's initial push delay ------------------------
2953
2954   // dynamically adjust player properties according to game engine version
2955   game.initial_push_delay_value =
2956     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2957
2958   // ---------- initialize changing elements ----------------------------------
2959
2960   // initialize changing elements information
2961   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2962   {
2963     struct ElementInfo *ei = &element_info[i];
2964
2965     // this pointer might have been changed in the level editor
2966     ei->change = &ei->change_page[0];
2967
2968     if (!IS_CUSTOM_ELEMENT(i))
2969     {
2970       ei->change->target_element = EL_EMPTY_SPACE;
2971       ei->change->delay_fixed = 0;
2972       ei->change->delay_random = 0;
2973       ei->change->delay_frames = 1;
2974     }
2975
2976     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2977     {
2978       ei->has_change_event[j] = FALSE;
2979
2980       ei->event_page_nr[j] = 0;
2981       ei->event_page[j] = &ei->change_page[0];
2982     }
2983   }
2984
2985   // add changing elements from pre-defined list
2986   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2987   {
2988     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2989     struct ElementInfo *ei = &element_info[ch_delay->element];
2990
2991     ei->change->target_element       = ch_delay->target_element;
2992     ei->change->delay_fixed          = ch_delay->change_delay;
2993
2994     ei->change->pre_change_function  = ch_delay->pre_change_function;
2995     ei->change->change_function      = ch_delay->change_function;
2996     ei->change->post_change_function = ch_delay->post_change_function;
2997
2998     ei->change->can_change = TRUE;
2999     ei->change->can_change_or_has_action = TRUE;
3000
3001     ei->has_change_event[CE_DELAY] = TRUE;
3002
3003     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3004     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3005   }
3006
3007   // ---------- initialize internal run-time variables ------------------------
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3012
3013     for (j = 0; j < ei->num_change_pages; j++)
3014     {
3015       ei->change_page[j].can_change_or_has_action =
3016         (ei->change_page[j].can_change |
3017          ei->change_page[j].has_action);
3018     }
3019   }
3020
3021   // add change events from custom element configuration
3022   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3023   {
3024     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3025
3026     for (j = 0; j < ei->num_change_pages; j++)
3027     {
3028       if (!ei->change_page[j].can_change_or_has_action)
3029         continue;
3030
3031       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3032       {
3033         // only add event page for the first page found with this event
3034         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3035         {
3036           ei->has_change_event[k] = TRUE;
3037
3038           ei->event_page_nr[k] = j;
3039           ei->event_page[k] = &ei->change_page[j];
3040         }
3041       }
3042     }
3043   }
3044
3045   // ---------- initialize reference elements in change conditions ------------
3046
3047   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3048   {
3049     int element = EL_CUSTOM_START + i;
3050     struct ElementInfo *ei = &element_info[element];
3051
3052     for (j = 0; j < ei->num_change_pages; j++)
3053     {
3054       int trigger_element = ei->change_page[j].initial_trigger_element;
3055
3056       if (trigger_element >= EL_PREV_CE_8 &&
3057           trigger_element <= EL_NEXT_CE_8)
3058         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3059
3060       ei->change_page[j].trigger_element = trigger_element;
3061     }
3062   }
3063
3064   // ---------- initialize run-time trigger player and element ----------------
3065
3066   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3067   {
3068     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3069
3070     for (j = 0; j < ei->num_change_pages; j++)
3071     {
3072       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3073       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3074       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3075       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3076       ei->change_page[j].actual_trigger_ce_value = 0;
3077       ei->change_page[j].actual_trigger_ce_score = 0;
3078     }
3079   }
3080
3081   // ---------- initialize trigger events -------------------------------------
3082
3083   // initialize trigger events information
3084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3085     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3086       trigger_events[i][j] = FALSE;
3087
3088   // add trigger events from element change event properties
3089   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3090   {
3091     struct ElementInfo *ei = &element_info[i];
3092
3093     for (j = 0; j < ei->num_change_pages; j++)
3094     {
3095       if (!ei->change_page[j].can_change_or_has_action)
3096         continue;
3097
3098       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3099       {
3100         int trigger_element = ei->change_page[j].trigger_element;
3101
3102         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3103         {
3104           if (ei->change_page[j].has_event[k])
3105           {
3106             if (IS_GROUP_ELEMENT(trigger_element))
3107             {
3108               struct ElementGroupInfo *group =
3109                 element_info[trigger_element].group;
3110
3111               for (l = 0; l < group->num_elements_resolved; l++)
3112                 trigger_events[group->element_resolved[l]][k] = TRUE;
3113             }
3114             else if (trigger_element == EL_ANY_ELEMENT)
3115               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3116                 trigger_events[l][k] = TRUE;
3117             else
3118               trigger_events[trigger_element][k] = TRUE;
3119           }
3120         }
3121       }
3122     }
3123   }
3124
3125   // ---------- initialize push delay -----------------------------------------
3126
3127   // initialize push delay values to default
3128   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3129   {
3130     if (!IS_CUSTOM_ELEMENT(i))
3131     {
3132       // set default push delay values (corrected since version 3.0.7-1)
3133       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3134       {
3135         element_info[i].push_delay_fixed = 2;
3136         element_info[i].push_delay_random = 8;
3137       }
3138       else
3139       {
3140         element_info[i].push_delay_fixed = 8;
3141         element_info[i].push_delay_random = 8;
3142       }
3143     }
3144   }
3145
3146   // set push delay value for certain elements from pre-defined list
3147   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3148   {
3149     int e = push_delay_list[i].element;
3150
3151     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3152     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3153   }
3154
3155   // set push delay value for Supaplex elements for newer engine versions
3156   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3157   {
3158     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     {
3160       if (IS_SP_ELEMENT(i))
3161       {
3162         // set SP push delay to just enough to push under a falling zonk
3163         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3164
3165         element_info[i].push_delay_fixed  = delay;
3166         element_info[i].push_delay_random = 0;
3167       }
3168     }
3169   }
3170
3171   // ---------- initialize move stepsize --------------------------------------
3172
3173   // initialize move stepsize values to default
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175     if (!IS_CUSTOM_ELEMENT(i))
3176       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3177
3178   // set move stepsize value for certain elements from pre-defined list
3179   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3180   {
3181     int e = move_stepsize_list[i].element;
3182
3183     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3184   }
3185
3186   // ---------- initialize collect score --------------------------------------
3187
3188   // initialize collect score values for custom elements from initial value
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_score = element_info[i].collect_score_initial;
3192
3193   // ---------- initialize collect count --------------------------------------
3194
3195   // initialize collect count values for non-custom elements
3196   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3197     if (!IS_CUSTOM_ELEMENT(i))
3198       element_info[i].collect_count_initial = 0;
3199
3200   // add collect count values for all elements from pre-defined list
3201   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3202     element_info[collect_count_list[i].element].collect_count_initial =
3203       collect_count_list[i].count;
3204
3205   // ---------- initialize access direction -----------------------------------
3206
3207   // initialize access direction values to default (access from every side)
3208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3209     if (!IS_CUSTOM_ELEMENT(i))
3210       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3211
3212   // set access direction value for certain elements from pre-defined list
3213   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3214     element_info[access_direction_list[i].element].access_direction =
3215       access_direction_list[i].direction;
3216
3217   // ---------- initialize explosion content ----------------------------------
3218   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3219   {
3220     if (IS_CUSTOM_ELEMENT(i))
3221       continue;
3222
3223     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3224     {
3225       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3226
3227       element_info[i].content.e[x][y] =
3228         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3229          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3230          i == EL_PLAYER_3 ? EL_EMERALD :
3231          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3232          i == EL_MOLE ? EL_EMERALD_RED :
3233          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3234          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3235          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3236          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3237          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3238          i == EL_WALL_EMERALD ? EL_EMERALD :
3239          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3240          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3241          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3242          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3243          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3244          i == EL_WALL_PEARL ? EL_PEARL :
3245          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3246          EL_EMPTY);
3247     }
3248   }
3249
3250   // ---------- initialize recursion detection --------------------------------
3251   recursion_loop_depth = 0;
3252   recursion_loop_detected = FALSE;
3253   recursion_loop_element = EL_UNDEFINED;
3254
3255   // ---------- initialize graphics engine ------------------------------------
3256   game.scroll_delay_value =
3257     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3258      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3259      !setup.forced_scroll_delay           ? 0 :
3260      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3261   game.scroll_delay_value =
3262     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3263
3264   // ---------- initialize game engine snapshots ------------------------------
3265   for (i = 0; i < MAX_PLAYERS; i++)
3266     game.snapshot.last_action[i] = 0;
3267   game.snapshot.changed_action = FALSE;
3268   game.snapshot.collected_item = FALSE;
3269   game.snapshot.mode =
3270     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3271      SNAPSHOT_MODE_EVERY_STEP :
3272      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3273      SNAPSHOT_MODE_EVERY_MOVE :
3274      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3275      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3276   game.snapshot.save_snapshot = FALSE;
3277
3278   // ---------- initialize level time for Supaplex engine ---------------------
3279   // Supaplex levels with time limit currently unsupported -- should be added
3280   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3281     level.time = 0;
3282 }
3283
3284 static int get_num_special_action(int element, int action_first,
3285                                   int action_last)
3286 {
3287   int num_special_action = 0;
3288   int i, j;
3289
3290   for (i = action_first; i <= action_last; i++)
3291   {
3292     boolean found = FALSE;
3293
3294     for (j = 0; j < NUM_DIRECTIONS; j++)
3295       if (el_act_dir2img(element, i, j) !=
3296           el_act_dir2img(element, ACTION_DEFAULT, j))
3297         found = TRUE;
3298
3299     if (found)
3300       num_special_action++;
3301     else
3302       break;
3303   }
3304
3305   return num_special_action;
3306 }
3307
3308
3309 // ============================================================================
3310 // InitGame()
3311 // ----------------------------------------------------------------------------
3312 // initialize and start new game
3313 // ============================================================================
3314
3315 #if DEBUG_INIT_PLAYER
3316 static void DebugPrintPlayerStatus(char *message)
3317 {
3318   int i;
3319
3320   if (!options.debug)
3321     return;
3322
3323   printf("%s:\n", message);
3324
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326   {
3327     struct PlayerInfo *player = &stored_player[i];
3328
3329     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3330            i + 1,
3331            player->present,
3332            player->connected,
3333            player->connected_locally,
3334            player->connected_network,
3335            player->active);
3336
3337     if (local_player == player)
3338       printf(" (local player)");
3339
3340     printf("\n");
3341   }
3342 }
3343 #endif
3344
3345 void InitGame(void)
3346 {
3347   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3348   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3349   int fade_mask = REDRAW_FIELD;
3350
3351   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3352   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3353   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3354   int initial_move_dir = MV_DOWN;
3355   int i, j, x, y;
3356
3357   // required here to update video display before fading (FIX THIS)
3358   DrawMaskedBorder(REDRAW_DOOR_2);
3359
3360   if (!game.restart_level)
3361     CloseDoor(DOOR_CLOSE_1);
3362
3363   SetGameStatus(GAME_MODE_PLAYING);
3364
3365   if (level_editor_test_game)
3366     FadeSkipNextFadeOut();
3367   else
3368     FadeSetEnterScreen();
3369
3370   if (CheckFadeAll())
3371     fade_mask = REDRAW_ALL;
3372
3373   FadeLevelSoundsAndMusic();
3374
3375   ExpireSoundLoops(TRUE);
3376
3377   FadeOut(fade_mask);
3378
3379   if (level_editor_test_game)
3380     FadeSkipNextFadeIn();
3381
3382   // needed if different viewport properties defined for playing
3383   ChangeViewportPropertiesIfNeeded();
3384
3385   ClearField();
3386
3387   DrawCompleteVideoDisplay();
3388
3389   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3390
3391   InitGameEngine();
3392   InitGameControlValues();
3393
3394   // don't play tapes over network
3395   network_playing = (network.enabled && !tape.playing);
3396
3397   for (i = 0; i < MAX_PLAYERS; i++)
3398   {
3399     struct PlayerInfo *player = &stored_player[i];
3400
3401     player->index_nr = i;
3402     player->index_bit = (1 << i);
3403     player->element_nr = EL_PLAYER_1 + i;
3404
3405     player->present = FALSE;
3406     player->active = FALSE;
3407     player->mapped = FALSE;
3408
3409     player->killed = FALSE;
3410     player->reanimated = FALSE;
3411     player->buried = FALSE;
3412
3413     player->action = 0;
3414     player->effective_action = 0;
3415     player->programmed_action = 0;
3416     player->snap_action = 0;
3417
3418     player->mouse_action.lx = 0;
3419     player->mouse_action.ly = 0;
3420     player->mouse_action.button = 0;
3421     player->mouse_action.button_hint = 0;
3422
3423     player->effective_mouse_action.lx = 0;
3424     player->effective_mouse_action.ly = 0;
3425     player->effective_mouse_action.button = 0;
3426     player->effective_mouse_action.button_hint = 0;
3427
3428     for (j = 0; j < MAX_NUM_KEYS; j++)
3429       player->key[j] = FALSE;
3430
3431     player->num_white_keys = 0;
3432
3433     player->dynabomb_count = 0;
3434     player->dynabomb_size = 1;
3435     player->dynabombs_left = 0;
3436     player->dynabomb_xl = FALSE;
3437
3438     player->MovDir = initial_move_dir;
3439     player->MovPos = 0;
3440     player->GfxPos = 0;
3441     player->GfxDir = initial_move_dir;
3442     player->GfxAction = ACTION_DEFAULT;
3443     player->Frame = 0;
3444     player->StepFrame = 0;
3445
3446     player->initial_element = player->element_nr;
3447     player->artwork_element =
3448       (level.use_artwork_element[i] ? level.artwork_element[i] :
3449        player->element_nr);
3450     player->use_murphy = FALSE;
3451
3452     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3453     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3454
3455     player->gravity = level.initial_player_gravity[i];
3456
3457     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3458
3459     player->actual_frame_counter = 0;
3460
3461     player->step_counter = 0;
3462
3463     player->last_move_dir = initial_move_dir;
3464
3465     player->is_active = FALSE;
3466
3467     player->is_waiting = FALSE;
3468     player->is_moving = FALSE;
3469     player->is_auto_moving = FALSE;
3470     player->is_digging = FALSE;
3471     player->is_snapping = FALSE;
3472     player->is_collecting = FALSE;
3473     player->is_pushing = FALSE;
3474     player->is_switching = FALSE;
3475     player->is_dropping = FALSE;
3476     player->is_dropping_pressed = FALSE;
3477
3478     player->is_bored = FALSE;
3479     player->is_sleeping = FALSE;
3480
3481     player->was_waiting = TRUE;
3482     player->was_moving = FALSE;
3483     player->was_snapping = FALSE;
3484     player->was_dropping = FALSE;
3485
3486     player->force_dropping = FALSE;
3487
3488     player->frame_counter_bored = -1;
3489     player->frame_counter_sleeping = -1;
3490
3491     player->anim_delay_counter = 0;
3492     player->post_delay_counter = 0;
3493
3494     player->dir_waiting = initial_move_dir;
3495     player->action_waiting = ACTION_DEFAULT;
3496     player->last_action_waiting = ACTION_DEFAULT;
3497     player->special_action_bored = ACTION_DEFAULT;
3498     player->special_action_sleeping = ACTION_DEFAULT;
3499
3500     player->switch_x = -1;
3501     player->switch_y = -1;
3502
3503     player->drop_x = -1;
3504     player->drop_y = -1;
3505
3506     player->show_envelope = 0;
3507
3508     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3509
3510     player->push_delay       = -1;      // initialized when pushing starts
3511     player->push_delay_value = game.initial_push_delay_value;
3512
3513     player->drop_delay = 0;
3514     player->drop_pressed_delay = 0;
3515
3516     player->last_jx = -1;
3517     player->last_jy = -1;
3518     player->jx = -1;
3519     player->jy = -1;
3520
3521     player->shield_normal_time_left = 0;
3522     player->shield_deadly_time_left = 0;
3523
3524     player->inventory_infinite_element = EL_UNDEFINED;
3525     player->inventory_size = 0;
3526
3527     if (level.use_initial_inventory[i])
3528     {
3529       for (j = 0; j < level.initial_inventory_size[i]; j++)
3530       {
3531         int element = level.initial_inventory_content[i][j];
3532         int collect_count = element_info[element].collect_count_initial;
3533         int k;
3534
3535         if (!IS_CUSTOM_ELEMENT(element))
3536           collect_count = 1;
3537
3538         if (collect_count == 0)
3539           player->inventory_infinite_element = element;
3540         else
3541           for (k = 0; k < collect_count; k++)
3542             if (player->inventory_size < MAX_INVENTORY_SIZE)
3543               player->inventory_element[player->inventory_size++] = element;
3544       }
3545     }
3546
3547     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3548     SnapField(player, 0, 0);
3549
3550     map_player_action[i] = i;
3551   }
3552
3553   network_player_action_received = FALSE;
3554
3555   // initial null action
3556   if (network_playing)
3557     SendToServer_MovePlayer(MV_NONE);
3558
3559   FrameCounter = 0;
3560   TimeFrames = 0;
3561   TimePlayed = 0;
3562   TimeLeft = level.time;
3563   TapeTime = 0;
3564
3565   ScreenMovDir = MV_NONE;
3566   ScreenMovPos = 0;
3567   ScreenGfxPos = 0;
3568
3569   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3570
3571   game.robot_wheel_x = -1;
3572   game.robot_wheel_y = -1;
3573
3574   game.exit_x = -1;
3575   game.exit_y = -1;
3576
3577   game.all_players_gone = FALSE;
3578
3579   game.LevelSolved = FALSE;
3580   game.GameOver = FALSE;
3581
3582   game.GamePlayed = !tape.playing;
3583
3584   game.LevelSolved_GameWon = FALSE;
3585   game.LevelSolved_GameEnd = FALSE;
3586   game.LevelSolved_SaveTape = FALSE;
3587   game.LevelSolved_SaveScore = FALSE;
3588
3589   game.LevelSolved_CountingTime = 0;
3590   game.LevelSolved_CountingScore = 0;
3591   game.LevelSolved_CountingHealth = 0;
3592
3593   game.panel.active = TRUE;
3594
3595   game.no_time_limit = (level.time == 0);
3596
3597   game.yamyam_content_nr = 0;
3598   game.robot_wheel_active = FALSE;
3599   game.magic_wall_active = FALSE;
3600   game.magic_wall_time_left = 0;
3601   game.light_time_left = 0;
3602   game.timegate_time_left = 0;
3603   game.switchgate_pos = 0;
3604   game.wind_direction = level.wind_direction_initial;
3605
3606   game.score = 0;
3607   game.score_final = 0;
3608
3609   game.health = MAX_HEALTH;
3610   game.health_final = MAX_HEALTH;
3611
3612   game.gems_still_needed = level.gems_needed;
3613   game.sokoban_fields_still_needed = 0;
3614   game.sokoban_objects_still_needed = 0;
3615   game.lights_still_needed = 0;
3616   game.players_still_needed = 0;
3617   game.friends_still_needed = 0;
3618
3619   game.lenses_time_left = 0;
3620   game.magnify_time_left = 0;
3621
3622   game.ball_state = level.ball_state_initial;
3623   game.ball_content_nr = 0;
3624
3625   game.explosions_delayed = TRUE;
3626
3627   game.envelope_active = FALSE;
3628
3629   for (i = 0; i < NUM_BELTS; i++)
3630   {
3631     game.belt_dir[i] = MV_NONE;
3632     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3633   }
3634
3635   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3636     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3637
3638 #if DEBUG_INIT_PLAYER
3639   DebugPrintPlayerStatus("Player status at level initialization");
3640 #endif
3641
3642   SCAN_PLAYFIELD(x, y)
3643   {
3644     Feld[x][y] = Last[x][y] = level.field[x][y];
3645     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3646     ChangeDelay[x][y] = 0;
3647     ChangePage[x][y] = -1;
3648     CustomValue[x][y] = 0;              // initialized in InitField()
3649     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3650     AmoebaNr[x][y] = 0;
3651     WasJustMoving[x][y] = 0;
3652     WasJustFalling[x][y] = 0;
3653     CheckCollision[x][y] = 0;
3654     CheckImpact[x][y] = 0;
3655     Stop[x][y] = FALSE;
3656     Pushed[x][y] = FALSE;
3657
3658     ChangeCount[x][y] = 0;
3659     ChangeEvent[x][y] = -1;
3660
3661     ExplodePhase[x][y] = 0;
3662     ExplodeDelay[x][y] = 0;
3663     ExplodeField[x][y] = EX_TYPE_NONE;
3664
3665     RunnerVisit[x][y] = 0;
3666     PlayerVisit[x][y] = 0;
3667
3668     GfxFrame[x][y] = 0;
3669     GfxRandom[x][y] = INIT_GFX_RANDOM();
3670     GfxElement[x][y] = EL_UNDEFINED;
3671     GfxAction[x][y] = ACTION_DEFAULT;
3672     GfxDir[x][y] = MV_NONE;
3673     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3674   }
3675
3676   SCAN_PLAYFIELD(x, y)
3677   {
3678     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3679       emulate_bd = FALSE;
3680     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3681       emulate_sb = FALSE;
3682     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3683       emulate_sp = FALSE;
3684
3685     InitField(x, y, TRUE);
3686
3687     ResetGfxAnimation(x, y);
3688   }
3689
3690   InitBeltMovement();
3691
3692   for (i = 0; i < MAX_PLAYERS; i++)
3693   {
3694     struct PlayerInfo *player = &stored_player[i];
3695
3696     // set number of special actions for bored and sleeping animation
3697     player->num_special_action_bored =
3698       get_num_special_action(player->artwork_element,
3699                              ACTION_BORING_1, ACTION_BORING_LAST);
3700     player->num_special_action_sleeping =
3701       get_num_special_action(player->artwork_element,
3702                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3703   }
3704
3705   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3706                     emulate_sb ? EMU_SOKOBAN :
3707                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3708
3709   // initialize type of slippery elements
3710   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3711   {
3712     if (!IS_CUSTOM_ELEMENT(i))
3713     {
3714       // default: elements slip down either to the left or right randomly
3715       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3716
3717       // SP style elements prefer to slip down on the left side
3718       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3719         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3720
3721       // BD style elements prefer to slip down on the left side
3722       if (game.emulation == EMU_BOULDERDASH)
3723         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3724     }
3725   }
3726
3727   // initialize explosion and ignition delay
3728   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3729   {
3730     if (!IS_CUSTOM_ELEMENT(i))
3731     {
3732       int num_phase = 8;
3733       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3734                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3735                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3736       int last_phase = (num_phase + 1) * delay;
3737       int half_phase = (num_phase / 2) * delay;
3738
3739       element_info[i].explosion_delay = last_phase - 1;
3740       element_info[i].ignition_delay = half_phase;
3741
3742       if (i == EL_BLACK_ORB)
3743         element_info[i].ignition_delay = 1;
3744     }
3745   }
3746
3747   // correct non-moving belts to start moving left
3748   for (i = 0; i < NUM_BELTS; i++)
3749     if (game.belt_dir[i] == MV_NONE)
3750       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3751
3752 #if USE_NEW_PLAYER_ASSIGNMENTS
3753   // use preferred player also in local single-player mode
3754   if (!network.enabled && !game.team_mode)
3755   {
3756     int old_index_nr = local_player->index_nr;
3757     int new_index_nr = setup.network_player_nr;
3758
3759     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3760     {
3761       stored_player[old_index_nr].connected_locally = FALSE;
3762       stored_player[new_index_nr].connected_locally = TRUE;
3763     }
3764   }
3765
3766   for (i = 0; i < MAX_PLAYERS; i++)
3767   {
3768     stored_player[i].connected = FALSE;
3769
3770     // in network game mode, the local player might not be the first player
3771     if (stored_player[i].connected_locally)
3772       local_player = &stored_player[i];
3773   }
3774
3775   if (!network.enabled)
3776     local_player->connected = TRUE;
3777
3778   if (tape.playing)
3779   {
3780     for (i = 0; i < MAX_PLAYERS; i++)
3781       stored_player[i].connected = tape.player_participates[i];
3782   }
3783   else if (network.enabled)
3784   {
3785     // add team mode players connected over the network (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 (stored_player[i].connected_network)
3790         stored_player[i].connected = TRUE;
3791   }
3792   else if (game.team_mode)
3793   {
3794     // try to guess locally connected team mode players (needed for correct
3795     // assignment of player figures from level to locally playing players)
3796
3797     for (i = 0; i < MAX_PLAYERS; i++)
3798       if (setup.input[i].use_joystick ||
3799           setup.input[i].key.left != KSYM_UNDEFINED)
3800         stored_player[i].connected = TRUE;
3801   }
3802
3803 #if DEBUG_INIT_PLAYER
3804   DebugPrintPlayerStatus("Player status after level initialization");
3805 #endif
3806
3807 #if DEBUG_INIT_PLAYER
3808   if (options.debug)
3809     printf("Reassigning players ...\n");
3810 #endif
3811
3812   // check if any connected player was not found in playfield
3813   for (i = 0; i < MAX_PLAYERS; i++)
3814   {
3815     struct PlayerInfo *player = &stored_player[i];
3816
3817     if (player->connected && !player->present)
3818     {
3819       struct PlayerInfo *field_player = NULL;
3820
3821 #if DEBUG_INIT_PLAYER
3822       if (options.debug)
3823         printf("- looking for field player for player %d ...\n", i + 1);
3824 #endif
3825
3826       // assign first free player found that is present in the playfield
3827
3828       // first try: look for unmapped playfield player that is not connected
3829       for (j = 0; j < MAX_PLAYERS; j++)
3830         if (field_player == NULL &&
3831             stored_player[j].present &&
3832             !stored_player[j].mapped &&
3833             !stored_player[j].connected)
3834           field_player = &stored_player[j];
3835
3836       // second try: look for *any* unmapped playfield player
3837       for (j = 0; j < MAX_PLAYERS; j++)
3838         if (field_player == NULL &&
3839             stored_player[j].present &&
3840             !stored_player[j].mapped)
3841           field_player = &stored_player[j];
3842
3843       if (field_player != NULL)
3844       {
3845         int jx = field_player->jx, jy = field_player->jy;
3846
3847 #if DEBUG_INIT_PLAYER
3848         if (options.debug)
3849           printf("- found player %d\n", field_player->index_nr + 1);
3850 #endif
3851
3852         player->present = FALSE;
3853         player->active = FALSE;
3854
3855         field_player->present = TRUE;
3856         field_player->active = TRUE;
3857
3858         /*
3859         player->initial_element = field_player->initial_element;
3860         player->artwork_element = field_player->artwork_element;
3861
3862         player->block_last_field       = field_player->block_last_field;
3863         player->block_delay_adjustment = field_player->block_delay_adjustment;
3864         */
3865
3866         StorePlayer[jx][jy] = field_player->element_nr;
3867
3868         field_player->jx = field_player->last_jx = jx;
3869         field_player->jy = field_player->last_jy = jy;
3870
3871         if (local_player == player)
3872           local_player = field_player;
3873
3874         map_player_action[field_player->index_nr] = i;
3875
3876         field_player->mapped = TRUE;
3877
3878 #if DEBUG_INIT_PLAYER
3879         if (options.debug)
3880           printf("- map_player_action[%d] == %d\n",
3881                  field_player->index_nr + 1, i + 1);
3882 #endif
3883       }
3884     }
3885
3886     if (player->connected && player->present)
3887       player->mapped = TRUE;
3888   }
3889
3890 #if DEBUG_INIT_PLAYER
3891   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3892 #endif
3893
3894 #else
3895
3896   // check if any connected player was not found in playfield
3897   for (i = 0; i < MAX_PLAYERS; i++)
3898   {
3899     struct PlayerInfo *player = &stored_player[i];
3900
3901     if (player->connected && !player->present)
3902     {
3903       for (j = 0; j < MAX_PLAYERS; j++)
3904       {
3905         struct PlayerInfo *field_player = &stored_player[j];
3906         int jx = field_player->jx, jy = field_player->jy;
3907
3908         // assign first free player found that is present in the playfield
3909         if (field_player->present && !field_player->connected)
3910         {
3911           player->present = TRUE;
3912           player->active = TRUE;
3913
3914           field_player->present = FALSE;
3915           field_player->active = FALSE;
3916
3917           player->initial_element = field_player->initial_element;
3918           player->artwork_element = field_player->artwork_element;
3919
3920           player->block_last_field       = field_player->block_last_field;
3921           player->block_delay_adjustment = field_player->block_delay_adjustment;
3922
3923           StorePlayer[jx][jy] = player->element_nr;
3924
3925           player->jx = player->last_jx = jx;
3926           player->jy = player->last_jy = jy;
3927
3928           break;
3929         }
3930       }
3931     }
3932   }
3933 #endif
3934
3935 #if 0
3936   printf("::: local_player->present == %d\n", local_player->present);
3937 #endif
3938
3939   // set focus to local player for network games, else to all players
3940   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3941   game.centered_player_nr_next = game.centered_player_nr;
3942   game.set_centered_player = FALSE;
3943   game.set_centered_player_wrap = FALSE;
3944
3945   if (network_playing && tape.recording)
3946   {
3947     // store client dependent player focus when recording network games
3948     tape.centered_player_nr_next = game.centered_player_nr_next;
3949     tape.set_centered_player = TRUE;
3950   }
3951
3952   if (tape.playing)
3953   {
3954     // when playing a tape, eliminate all players who do not participate
3955
3956 #if USE_NEW_PLAYER_ASSIGNMENTS
3957
3958     if (!game.team_mode)
3959     {
3960       for (i = 0; i < MAX_PLAYERS; i++)
3961       {
3962         if (stored_player[i].active &&
3963             !tape.player_participates[map_player_action[i]])
3964         {
3965           struct PlayerInfo *player = &stored_player[i];
3966           int jx = player->jx, jy = player->jy;
3967
3968 #if DEBUG_INIT_PLAYER
3969           if (options.debug)
3970             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3971 #endif
3972
3973           player->active = FALSE;
3974           StorePlayer[jx][jy] = 0;
3975           Feld[jx][jy] = EL_EMPTY;
3976         }
3977       }
3978     }
3979
3980 #else
3981
3982     for (i = 0; i < MAX_PLAYERS; i++)
3983     {
3984       if (stored_player[i].active &&
3985           !tape.player_participates[i])
3986       {
3987         struct PlayerInfo *player = &stored_player[i];
3988         int jx = player->jx, jy = player->jy;
3989
3990         player->active = FALSE;
3991         StorePlayer[jx][jy] = 0;
3992         Feld[jx][jy] = EL_EMPTY;
3993       }
3994     }
3995 #endif
3996   }
3997   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3998   {
3999     // when in single player mode, eliminate all but the local player
4000
4001     for (i = 0; i < MAX_PLAYERS; i++)
4002     {
4003       struct PlayerInfo *player = &stored_player[i];
4004
4005       if (player->active && player != local_player)
4006       {
4007         int jx = player->jx, jy = player->jy;
4008
4009         player->active = FALSE;
4010         player->present = FALSE;
4011
4012         StorePlayer[jx][jy] = 0;
4013         Feld[jx][jy] = EL_EMPTY;
4014       }
4015     }
4016   }
4017
4018   for (i = 0; i < MAX_PLAYERS; i++)
4019     if (stored_player[i].active)
4020       game.players_still_needed++;
4021
4022   if (level.solved_by_one_player)
4023     game.players_still_needed = 1;
4024
4025   // when recording the game, store which players take part in the game
4026   if (tape.recording)
4027   {
4028 #if USE_NEW_PLAYER_ASSIGNMENTS
4029     for (i = 0; i < MAX_PLAYERS; i++)
4030       if (stored_player[i].connected)
4031         tape.player_participates[i] = TRUE;
4032 #else
4033     for (i = 0; i < MAX_PLAYERS; i++)
4034       if (stored_player[i].active)
4035         tape.player_participates[i] = TRUE;
4036 #endif
4037   }
4038
4039 #if DEBUG_INIT_PLAYER
4040   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4041 #endif
4042
4043   if (BorderElement == EL_EMPTY)
4044   {
4045     SBX_Left = 0;
4046     SBX_Right = lev_fieldx - SCR_FIELDX;
4047     SBY_Upper = 0;
4048     SBY_Lower = lev_fieldy - SCR_FIELDY;
4049   }
4050   else
4051   {
4052     SBX_Left = -1;
4053     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4054     SBY_Upper = -1;
4055     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4056   }
4057
4058   if (full_lev_fieldx <= SCR_FIELDX)
4059     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4060   if (full_lev_fieldy <= SCR_FIELDY)
4061     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4062
4063   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4064     SBX_Left--;
4065   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4066     SBY_Upper--;
4067
4068   // if local player not found, look for custom element that might create
4069   // the player (make some assumptions about the right custom element)
4070   if (!local_player->present)
4071   {
4072     int start_x = 0, start_y = 0;
4073     int found_rating = 0;
4074     int found_element = EL_UNDEFINED;
4075     int player_nr = local_player->index_nr;
4076
4077     SCAN_PLAYFIELD(x, y)
4078     {
4079       int element = Feld[x][y];
4080       int content;
4081       int xx, yy;
4082       boolean is_player;
4083
4084       if (level.use_start_element[player_nr] &&
4085           level.start_element[player_nr] == element &&
4086           found_rating < 4)
4087       {
4088         start_x = x;
4089         start_y = y;
4090
4091         found_rating = 4;
4092         found_element = element;
4093       }
4094
4095       if (!IS_CUSTOM_ELEMENT(element))
4096         continue;
4097
4098       if (CAN_CHANGE(element))
4099       {
4100         for (i = 0; i < element_info[element].num_change_pages; i++)
4101         {
4102           // check for player created from custom element as single target
4103           content = element_info[element].change_page[i].target_element;
4104           is_player = ELEM_IS_PLAYER(content);
4105
4106           if (is_player && (found_rating < 3 ||
4107                             (found_rating == 3 && element < found_element)))
4108           {
4109             start_x = x;
4110             start_y = y;
4111
4112             found_rating = 3;
4113             found_element = element;
4114           }
4115         }
4116       }
4117
4118       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4119       {
4120         // check for player created from custom element as explosion content
4121         content = element_info[element].content.e[xx][yy];
4122         is_player = ELEM_IS_PLAYER(content);
4123
4124         if (is_player && (found_rating < 2 ||
4125                           (found_rating == 2 && element < found_element)))
4126         {
4127           start_x = x + xx - 1;
4128           start_y = y + yy - 1;
4129
4130           found_rating = 2;
4131           found_element = element;
4132         }
4133
4134         if (!CAN_CHANGE(element))
4135           continue;
4136
4137         for (i = 0; i < element_info[element].num_change_pages; i++)
4138         {
4139           // check for player created from custom element as extended target
4140           content =
4141             element_info[element].change_page[i].target_content.e[xx][yy];
4142
4143           is_player = ELEM_IS_PLAYER(content);
4144
4145           if (is_player && (found_rating < 1 ||
4146                             (found_rating == 1 && element < found_element)))
4147           {
4148             start_x = x + xx - 1;
4149             start_y = y + yy - 1;
4150
4151             found_rating = 1;
4152             found_element = element;
4153           }
4154         }
4155       }
4156     }
4157
4158     scroll_x = SCROLL_POSITION_X(start_x);
4159     scroll_y = SCROLL_POSITION_Y(start_y);
4160   }
4161   else
4162   {
4163     scroll_x = SCROLL_POSITION_X(local_player->jx);
4164     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4165   }
4166
4167   // !!! FIX THIS (START) !!!
4168   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4169   {
4170     InitGameEngine_EM();
4171   }
4172   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4173   {
4174     InitGameEngine_SP();
4175   }
4176   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4177   {
4178     InitGameEngine_MM();
4179   }
4180   else
4181   {
4182     DrawLevel(REDRAW_FIELD);
4183     DrawAllPlayers();
4184
4185     // after drawing the level, correct some elements
4186     if (game.timegate_time_left == 0)
4187       CloseAllOpenTimegates();
4188   }
4189
4190   // blit playfield from scroll buffer to normal back buffer for fading in
4191   BlitScreenToBitmap(backbuffer);
4192   // !!! FIX THIS (END) !!!
4193
4194   DrawMaskedBorder(fade_mask);
4195
4196   FadeIn(fade_mask);
4197
4198 #if 1
4199   // full screen redraw is required at this point in the following cases:
4200   // - special editor door undrawn when game was started from level editor
4201   // - drawing area (playfield) was changed and has to be removed completely
4202   redraw_mask = REDRAW_ALL;
4203   BackToFront();
4204 #endif
4205
4206   if (!game.restart_level)
4207   {
4208     // copy default game door content to main double buffer
4209
4210     // !!! CHECK AGAIN !!!
4211     SetPanelBackground();
4212     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4213     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4214   }
4215
4216   SetPanelBackground();
4217   SetDrawBackgroundMask(REDRAW_DOOR_1);
4218
4219   UpdateAndDisplayGameControlValues();
4220
4221   if (!game.restart_level)
4222   {
4223     UnmapGameButtons();
4224     UnmapTapeButtons();
4225
4226     FreeGameButtons();
4227     CreateGameButtons();
4228
4229     MapGameButtons();
4230     MapTapeButtons();
4231
4232     // copy actual game door content to door double buffer for OpenDoor()
4233     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4234
4235     OpenDoor(DOOR_OPEN_ALL);
4236
4237     KeyboardAutoRepeatOffUnlessAutoplay();
4238
4239 #if DEBUG_INIT_PLAYER
4240     DebugPrintPlayerStatus("Player status (final)");
4241 #endif
4242   }
4243
4244   UnmapAllGadgets();
4245
4246   MapGameButtons();
4247   MapTapeButtons();
4248
4249   if (!game.restart_level && !tape.playing)
4250   {
4251     LevelStats_incPlayed(level_nr);
4252
4253     SaveLevelSetup_SeriesInfo();
4254   }
4255
4256   game.restart_level = FALSE;
4257   game.restart_game_message = NULL;
4258   game.request_active = FALSE;
4259
4260   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4261     InitGameActions_MM();
4262
4263   SaveEngineSnapshotToListInitial();
4264
4265   if (!game.restart_level)
4266   {
4267     PlaySound(SND_GAME_STARTING);
4268
4269     if (setup.sound_music)
4270       PlayLevelMusic();
4271   }
4272 }
4273
4274 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4275                         int actual_player_x, int actual_player_y)
4276 {
4277   // this is used for non-R'n'D game engines to update certain engine values
4278
4279   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4280   {
4281     actual_player_x = correctLevelPosX_EM(actual_player_x);
4282     actual_player_y = correctLevelPosY_EM(actual_player_y);
4283   }
4284
4285   // needed to determine if sounds are played within the visible screen area
4286   scroll_x = actual_scroll_x;
4287   scroll_y = actual_scroll_y;
4288
4289   // needed to get player position for "follow finger" playing input method
4290   local_player->jx = actual_player_x;
4291   local_player->jy = actual_player_y;
4292 }
4293
4294 void InitMovDir(int x, int y)
4295 {
4296   int i, element = Feld[x][y];
4297   static int xy[4][2] =
4298   {
4299     {  0, +1 },
4300     { +1,  0 },
4301     {  0, -1 },
4302     { -1,  0 }
4303   };
4304   static int direction[3][4] =
4305   {
4306     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4307     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4308     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4309   };
4310
4311   switch (element)
4312   {
4313     case EL_BUG_RIGHT:
4314     case EL_BUG_UP:
4315     case EL_BUG_LEFT:
4316     case EL_BUG_DOWN:
4317       Feld[x][y] = EL_BUG;
4318       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4319       break;
4320
4321     case EL_SPACESHIP_RIGHT:
4322     case EL_SPACESHIP_UP:
4323     case EL_SPACESHIP_LEFT:
4324     case EL_SPACESHIP_DOWN:
4325       Feld[x][y] = EL_SPACESHIP;
4326       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4327       break;
4328
4329     case EL_BD_BUTTERFLY_RIGHT:
4330     case EL_BD_BUTTERFLY_UP:
4331     case EL_BD_BUTTERFLY_LEFT:
4332     case EL_BD_BUTTERFLY_DOWN:
4333       Feld[x][y] = EL_BD_BUTTERFLY;
4334       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4335       break;
4336
4337     case EL_BD_FIREFLY_RIGHT:
4338     case EL_BD_FIREFLY_UP:
4339     case EL_BD_FIREFLY_LEFT:
4340     case EL_BD_FIREFLY_DOWN:
4341       Feld[x][y] = EL_BD_FIREFLY;
4342       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4343       break;
4344
4345     case EL_PACMAN_RIGHT:
4346     case EL_PACMAN_UP:
4347     case EL_PACMAN_LEFT:
4348     case EL_PACMAN_DOWN:
4349       Feld[x][y] = EL_PACMAN;
4350       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4351       break;
4352
4353     case EL_YAMYAM_LEFT:
4354     case EL_YAMYAM_RIGHT:
4355     case EL_YAMYAM_UP:
4356     case EL_YAMYAM_DOWN:
4357       Feld[x][y] = EL_YAMYAM;
4358       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4359       break;
4360
4361     case EL_SP_SNIKSNAK:
4362       MovDir[x][y] = MV_UP;
4363       break;
4364
4365     case EL_SP_ELECTRON:
4366       MovDir[x][y] = MV_LEFT;
4367       break;
4368
4369     case EL_MOLE_LEFT:
4370     case EL_MOLE_RIGHT:
4371     case EL_MOLE_UP:
4372     case EL_MOLE_DOWN:
4373       Feld[x][y] = EL_MOLE;
4374       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4375       break;
4376
4377     default:
4378       if (IS_CUSTOM_ELEMENT(element))
4379       {
4380         struct ElementInfo *ei = &element_info[element];
4381         int move_direction_initial = ei->move_direction_initial;
4382         int move_pattern = ei->move_pattern;
4383
4384         if (move_direction_initial == MV_START_PREVIOUS)
4385         {
4386           if (MovDir[x][y] != MV_NONE)
4387             return;
4388
4389           move_direction_initial = MV_START_AUTOMATIC;
4390         }
4391
4392         if (move_direction_initial == MV_START_RANDOM)
4393           MovDir[x][y] = 1 << RND(4);
4394         else if (move_direction_initial & MV_ANY_DIRECTION)
4395           MovDir[x][y] = move_direction_initial;
4396         else if (move_pattern == MV_ALL_DIRECTIONS ||
4397                  move_pattern == MV_TURNING_LEFT ||
4398                  move_pattern == MV_TURNING_RIGHT ||
4399                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4400                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4401                  move_pattern == MV_TURNING_RANDOM)
4402           MovDir[x][y] = 1 << RND(4);
4403         else if (move_pattern == MV_HORIZONTAL)
4404           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4405         else if (move_pattern == MV_VERTICAL)
4406           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4407         else if (move_pattern & MV_ANY_DIRECTION)
4408           MovDir[x][y] = element_info[element].move_pattern;
4409         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4410                  move_pattern == MV_ALONG_RIGHT_SIDE)
4411         {
4412           // use random direction as default start direction
4413           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4414             MovDir[x][y] = 1 << RND(4);
4415
4416           for (i = 0; i < NUM_DIRECTIONS; i++)
4417           {
4418             int x1 = x + xy[i][0];
4419             int y1 = y + xy[i][1];
4420
4421             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4422             {
4423               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4424                 MovDir[x][y] = direction[0][i];
4425               else
4426                 MovDir[x][y] = direction[1][i];
4427
4428               break;
4429             }
4430           }
4431         }                
4432       }
4433       else
4434       {
4435         MovDir[x][y] = 1 << RND(4);
4436
4437         if (element != EL_BUG &&
4438             element != EL_SPACESHIP &&
4439             element != EL_BD_BUTTERFLY &&
4440             element != EL_BD_FIREFLY)
4441           break;
4442
4443         for (i = 0; i < NUM_DIRECTIONS; i++)
4444         {
4445           int x1 = x + xy[i][0];
4446           int y1 = y + xy[i][1];
4447
4448           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4449           {
4450             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4451             {
4452               MovDir[x][y] = direction[0][i];
4453               break;
4454             }
4455             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4456                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4457             {
4458               MovDir[x][y] = direction[1][i];
4459               break;
4460             }
4461           }
4462         }
4463       }
4464       break;
4465   }
4466
4467   GfxDir[x][y] = MovDir[x][y];
4468 }
4469
4470 void InitAmoebaNr(int x, int y)
4471 {
4472   int i;
4473   int group_nr = AmoebeNachbarNr(x, y);
4474
4475   if (group_nr == 0)
4476   {
4477     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4478     {
4479       if (AmoebaCnt[i] == 0)
4480       {
4481         group_nr = i;
4482         break;
4483       }
4484     }
4485   }
4486
4487   AmoebaNr[x][y] = group_nr;
4488   AmoebaCnt[group_nr]++;
4489   AmoebaCnt2[group_nr]++;
4490 }
4491
4492 static void LevelSolved(void)
4493 {
4494   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4495       game.players_still_needed > 0)
4496     return;
4497
4498   game.LevelSolved = TRUE;
4499   game.GameOver = TRUE;
4500
4501   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4502                       game_em.lev->score :
4503                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4504                       game_mm.score :
4505                       game.score);
4506   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4507                        MM_HEALTH(game_mm.laser_overload_value) :
4508                        game.health);
4509
4510   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4511   game.LevelSolved_CountingScore = game.score_final;
4512   game.LevelSolved_CountingHealth = game.health_final;
4513 }
4514
4515 void GameWon(void)
4516 {
4517   static int time_count_steps;
4518   static int time, time_final;
4519   static int score, score_final;
4520   static int health, health_final;
4521   static int game_over_delay_1 = 0;
4522   static int game_over_delay_2 = 0;
4523   static int game_over_delay_3 = 0;
4524   int game_over_delay_value_1 = 50;
4525   int game_over_delay_value_2 = 25;
4526   int game_over_delay_value_3 = 50;
4527
4528   if (!game.LevelSolved_GameWon)
4529   {
4530     int i;
4531
4532     // do not start end game actions before the player stops moving (to exit)
4533     if (local_player->active && local_player->MovPos)
4534       return;
4535
4536     game.LevelSolved_GameWon = TRUE;
4537     game.LevelSolved_SaveTape = tape.recording;
4538     game.LevelSolved_SaveScore = !tape.playing;
4539
4540     if (!tape.playing)
4541     {
4542       LevelStats_incSolved(level_nr);
4543
4544       SaveLevelSetup_SeriesInfo();
4545     }
4546
4547     if (tape.auto_play)         // tape might already be stopped here
4548       tape.auto_play_level_solved = TRUE;
4549
4550     TapeStop();
4551
4552     game_over_delay_1 = 0;
4553     game_over_delay_2 = 0;
4554     game_over_delay_3 = game_over_delay_value_3;
4555
4556     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4557     score = score_final = game.score_final;
4558     health = health_final = game.health_final;
4559
4560     if (level.score[SC_TIME_BONUS] > 0)
4561     {
4562       if (TimeLeft > 0)
4563       {
4564         time_final = 0;
4565         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4566       }
4567       else if (game.no_time_limit && TimePlayed < 999)
4568       {
4569         time_final = 999;
4570         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4571       }
4572
4573       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4574
4575       game_over_delay_1 = game_over_delay_value_1;
4576
4577       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4578       {
4579         health_final = 0;
4580         score_final += health * level.score[SC_TIME_BONUS];
4581
4582         game_over_delay_2 = game_over_delay_value_2;
4583       }
4584
4585       game.score_final = score_final;
4586       game.health_final = health_final;
4587     }
4588
4589     if (level_editor_test_game)
4590     {
4591       time = time_final;
4592       score = score_final;
4593
4594       game.LevelSolved_CountingTime = time;
4595       game.LevelSolved_CountingScore = score;
4596
4597       game_panel_controls[GAME_PANEL_TIME].value = time;
4598       game_panel_controls[GAME_PANEL_SCORE].value = score;
4599
4600       DisplayGameControlValues();
4601     }
4602
4603     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4604     {
4605       // check if last player has left the level
4606       if (game.exit_x >= 0 &&
4607           game.exit_y >= 0)
4608       {
4609         int x = game.exit_x;
4610         int y = game.exit_y;
4611         int element = Feld[x][y];
4612
4613         // close exit door after last player
4614         if ((game.all_players_gone &&
4615              (element == EL_EXIT_OPEN ||
4616               element == EL_SP_EXIT_OPEN ||
4617               element == EL_STEEL_EXIT_OPEN)) ||
4618             element == EL_EM_EXIT_OPEN ||
4619             element == EL_EM_STEEL_EXIT_OPEN)
4620         {
4621
4622           Feld[x][y] =
4623             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4624              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4625              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4626              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4627              EL_EM_STEEL_EXIT_CLOSING);
4628
4629           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4630         }
4631
4632         // player disappears
4633         DrawLevelField(x, y);
4634       }
4635
4636       for (i = 0; i < MAX_PLAYERS; i++)
4637       {
4638         struct PlayerInfo *player = &stored_player[i];
4639
4640         if (player->present)
4641         {
4642           RemovePlayer(player);
4643
4644           // player disappears
4645           DrawLevelField(player->jx, player->jy);
4646         }
4647       }
4648     }
4649
4650     PlaySound(SND_GAME_WINNING);
4651   }
4652
4653   if (game_over_delay_1 > 0)
4654   {
4655     game_over_delay_1--;
4656
4657     return;
4658   }
4659
4660   if (time != time_final)
4661   {
4662     int time_to_go = ABS(time_final - time);
4663     int time_count_dir = (time < time_final ? +1 : -1);
4664
4665     if (time_to_go < time_count_steps)
4666       time_count_steps = 1;
4667
4668     time  += time_count_steps * time_count_dir;
4669     score += time_count_steps * level.score[SC_TIME_BONUS];
4670
4671     game.LevelSolved_CountingTime = time;
4672     game.LevelSolved_CountingScore = score;
4673
4674     game_panel_controls[GAME_PANEL_TIME].value = time;
4675     game_panel_controls[GAME_PANEL_SCORE].value = score;
4676
4677     DisplayGameControlValues();
4678
4679     if (time == time_final)
4680       StopSound(SND_GAME_LEVELTIME_BONUS);
4681     else if (setup.sound_loops)
4682       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4683     else
4684       PlaySound(SND_GAME_LEVELTIME_BONUS);
4685
4686     return;
4687   }
4688
4689   if (game_over_delay_2 > 0)
4690   {
4691     game_over_delay_2--;
4692
4693     return;
4694   }
4695
4696   if (health != health_final)
4697   {
4698     int health_count_dir = (health < health_final ? +1 : -1);
4699
4700     health += health_count_dir;
4701     score  += level.score[SC_TIME_BONUS];
4702
4703     game.LevelSolved_CountingHealth = health;
4704     game.LevelSolved_CountingScore = score;
4705
4706     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4707     game_panel_controls[GAME_PANEL_SCORE].value = score;
4708
4709     DisplayGameControlValues();
4710
4711     if (health == health_final)
4712       StopSound(SND_GAME_LEVELTIME_BONUS);
4713     else if (setup.sound_loops)
4714       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4715     else
4716       PlaySound(SND_GAME_LEVELTIME_BONUS);
4717
4718     return;
4719   }
4720
4721   game.panel.active = FALSE;
4722
4723   if (game_over_delay_3 > 0)
4724   {
4725     game_over_delay_3--;
4726
4727     return;
4728   }
4729
4730   GameEnd();
4731 }
4732
4733 void GameEnd(void)
4734 {
4735   // used instead of "level_nr" (needed for network games)
4736   int last_level_nr = levelset.level_nr;
4737   int hi_pos;
4738
4739   game.LevelSolved_GameEnd = TRUE;
4740
4741   if (game.LevelSolved_SaveTape)
4742   {
4743     // make sure that request dialog to save tape does not open door again
4744     if (!global.use_envelope_request)
4745       CloseDoor(DOOR_CLOSE_1);
4746
4747     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4748   }
4749
4750   // if no tape is to be saved, close both doors simultaneously
4751   CloseDoor(DOOR_CLOSE_ALL);
4752
4753   if (level_editor_test_game)
4754   {
4755     SetGameStatus(GAME_MODE_MAIN);
4756
4757     DrawMainMenu();
4758
4759     return;
4760   }
4761
4762   if (!game.LevelSolved_SaveScore)
4763   {
4764     SetGameStatus(GAME_MODE_MAIN);
4765
4766     DrawMainMenu();
4767
4768     return;
4769   }
4770
4771   if (level_nr == leveldir_current->handicap_level)
4772   {
4773     leveldir_current->handicap_level++;
4774
4775     SaveLevelSetup_SeriesInfo();
4776   }
4777
4778   if (setup.increment_levels &&
4779       level_nr < leveldir_current->last_level &&
4780       !network_playing)
4781   {
4782     level_nr++;         // advance to next level
4783     TapeErase();        // start with empty tape
4784
4785     if (setup.auto_play_next_level)
4786     {
4787       LoadLevel(level_nr);
4788
4789       SaveLevelSetup_SeriesInfo();
4790     }
4791   }
4792
4793   hi_pos = NewHiScore(last_level_nr);
4794
4795   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4796   {
4797     SetGameStatus(GAME_MODE_SCORES);
4798
4799     DrawHallOfFame(last_level_nr, hi_pos);
4800   }
4801   else if (setup.auto_play_next_level && setup.increment_levels &&
4802            last_level_nr < leveldir_current->last_level &&
4803            !network_playing)
4804   {
4805     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4806   }
4807   else
4808   {
4809     SetGameStatus(GAME_MODE_MAIN);
4810
4811     DrawMainMenu();
4812   }
4813 }
4814
4815 int NewHiScore(int level_nr)
4816 {
4817   int k, l;
4818   int position = -1;
4819   boolean one_score_entry_per_name = !program.many_scores_per_name;
4820
4821   LoadScore(level_nr);
4822
4823   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4824       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4825     return -1;
4826
4827   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4828   {
4829     if (game.score_final > highscore[k].Score)
4830     {
4831       // player has made it to the hall of fame
4832
4833       if (k < MAX_SCORE_ENTRIES - 1)
4834       {
4835         int m = MAX_SCORE_ENTRIES - 1;
4836
4837         if (one_score_entry_per_name)
4838         {
4839           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4840             if (strEqual(setup.player_name, highscore[l].Name))
4841               m = l;
4842
4843           if (m == k)   // player's new highscore overwrites his old one
4844             goto put_into_list;
4845         }
4846
4847         for (l = m; l > k; l--)
4848         {
4849           strcpy(highscore[l].Name, highscore[l - 1].Name);
4850           highscore[l].Score = highscore[l - 1].Score;
4851         }
4852       }
4853
4854       put_into_list:
4855
4856       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4857       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4858       highscore[k].Score = game.score_final;
4859       position = k;
4860
4861       break;
4862     }
4863     else if (one_score_entry_per_name &&
4864              !strncmp(setup.player_name, highscore[k].Name,
4865                       MAX_PLAYER_NAME_LEN))
4866       break;    // player already there with a higher score
4867   }
4868
4869   if (position >= 0) 
4870     SaveScore(level_nr);
4871
4872   return position;
4873 }
4874
4875 static int getElementMoveStepsizeExt(int x, int y, int direction)
4876 {
4877   int element = Feld[x][y];
4878   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4879   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4880   int horiz_move = (dx != 0);
4881   int sign = (horiz_move ? dx : dy);
4882   int step = sign * element_info[element].move_stepsize;
4883
4884   // special values for move stepsize for spring and things on conveyor belt
4885   if (horiz_move)
4886   {
4887     if (CAN_FALL(element) &&
4888         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4889       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4890     else if (element == EL_SPRING)
4891       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4892   }
4893
4894   return step;
4895 }
4896
4897 static int getElementMoveStepsize(int x, int y)
4898 {
4899   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4900 }
4901
4902 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4903 {
4904   if (player->GfxAction != action || player->GfxDir != dir)
4905   {
4906     player->GfxAction = action;
4907     player->GfxDir = dir;
4908     player->Frame = 0;
4909     player->StepFrame = 0;
4910   }
4911 }
4912
4913 static void ResetGfxFrame(int x, int y)
4914 {
4915   // profiling showed that "autotest" spends 10~20% of its time in this function
4916   if (DrawingDeactivatedField())
4917     return;
4918
4919   int element = Feld[x][y];
4920   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4921
4922   if (graphic_info[graphic].anim_global_sync)
4923     GfxFrame[x][y] = FrameCounter;
4924   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4925     GfxFrame[x][y] = CustomValue[x][y];
4926   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4927     GfxFrame[x][y] = element_info[element].collect_score;
4928   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4929     GfxFrame[x][y] = ChangeDelay[x][y];
4930 }
4931
4932 static void ResetGfxAnimation(int x, int y)
4933 {
4934   GfxAction[x][y] = ACTION_DEFAULT;
4935   GfxDir[x][y] = MovDir[x][y];
4936   GfxFrame[x][y] = 0;
4937
4938   ResetGfxFrame(x, y);
4939 }
4940
4941 static void ResetRandomAnimationValue(int x, int y)
4942 {
4943   GfxRandom[x][y] = INIT_GFX_RANDOM();
4944 }
4945
4946 static void InitMovingField(int x, int y, int direction)
4947 {
4948   int element = Feld[x][y];
4949   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4950   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4951   int newx = x + dx;
4952   int newy = y + dy;
4953   boolean is_moving_before, is_moving_after;
4954
4955   // check if element was/is moving or being moved before/after mode change
4956   is_moving_before = (WasJustMoving[x][y] != 0);
4957   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4958
4959   // reset animation only for moving elements which change direction of moving
4960   // or which just started or stopped moving
4961   // (else CEs with property "can move" / "not moving" are reset each frame)
4962   if (is_moving_before != is_moving_after ||
4963       direction != MovDir[x][y])
4964     ResetGfxAnimation(x, y);
4965
4966   MovDir[x][y] = direction;
4967   GfxDir[x][y] = direction;
4968
4969   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4970                      direction == MV_DOWN && CAN_FALL(element) ?
4971                      ACTION_FALLING : ACTION_MOVING);
4972
4973   // this is needed for CEs with property "can move" / "not moving"
4974
4975   if (is_moving_after)
4976   {
4977     if (Feld[newx][newy] == EL_EMPTY)
4978       Feld[newx][newy] = EL_BLOCKED;
4979
4980     MovDir[newx][newy] = MovDir[x][y];
4981
4982     CustomValue[newx][newy] = CustomValue[x][y];
4983
4984     GfxFrame[newx][newy] = GfxFrame[x][y];
4985     GfxRandom[newx][newy] = GfxRandom[x][y];
4986     GfxAction[newx][newy] = GfxAction[x][y];
4987     GfxDir[newx][newy] = GfxDir[x][y];
4988   }
4989 }
4990
4991 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4992 {
4993   int direction = MovDir[x][y];
4994   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4995   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4996
4997   *goes_to_x = newx;
4998   *goes_to_y = newy;
4999 }
5000
5001 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5002 {
5003   int oldx = x, oldy = y;
5004   int direction = MovDir[x][y];
5005
5006   if (direction == MV_LEFT)
5007     oldx++;
5008   else if (direction == MV_RIGHT)
5009     oldx--;
5010   else if (direction == MV_UP)
5011     oldy++;
5012   else if (direction == MV_DOWN)
5013     oldy--;
5014
5015   *comes_from_x = oldx;
5016   *comes_from_y = oldy;
5017 }
5018
5019 static int MovingOrBlocked2Element(int x, int y)
5020 {
5021   int element = Feld[x][y];
5022
5023   if (element == EL_BLOCKED)
5024   {
5025     int oldx, oldy;
5026
5027     Blocked2Moving(x, y, &oldx, &oldy);
5028     return Feld[oldx][oldy];
5029   }
5030   else
5031     return element;
5032 }
5033
5034 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5035 {
5036   // like MovingOrBlocked2Element(), but if element is moving
5037   // and (x,y) is the field the moving element is just leaving,
5038   // return EL_BLOCKED instead of the element value
5039   int element = Feld[x][y];
5040
5041   if (IS_MOVING(x, y))
5042   {
5043     if (element == EL_BLOCKED)
5044     {
5045       int oldx, oldy;
5046
5047       Blocked2Moving(x, y, &oldx, &oldy);
5048       return Feld[oldx][oldy];
5049     }
5050     else
5051       return EL_BLOCKED;
5052   }
5053   else
5054     return element;
5055 }
5056
5057 static void RemoveField(int x, int y)
5058 {
5059   Feld[x][y] = EL_EMPTY;
5060
5061   MovPos[x][y] = 0;
5062   MovDir[x][y] = 0;
5063   MovDelay[x][y] = 0;
5064
5065   CustomValue[x][y] = 0;
5066
5067   AmoebaNr[x][y] = 0;
5068   ChangeDelay[x][y] = 0;
5069   ChangePage[x][y] = -1;
5070   Pushed[x][y] = FALSE;
5071
5072   GfxElement[x][y] = EL_UNDEFINED;
5073   GfxAction[x][y] = ACTION_DEFAULT;
5074   GfxDir[x][y] = MV_NONE;
5075 }
5076
5077 static void RemoveMovingField(int x, int y)
5078 {
5079   int oldx = x, oldy = y, newx = x, newy = y;
5080   int element = Feld[x][y];
5081   int next_element = EL_UNDEFINED;
5082
5083   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5084     return;
5085
5086   if (IS_MOVING(x, y))
5087   {
5088     Moving2Blocked(x, y, &newx, &newy);
5089
5090     if (Feld[newx][newy] != EL_BLOCKED)
5091     {
5092       // element is moving, but target field is not free (blocked), but
5093       // already occupied by something different (example: acid pool);
5094       // in this case, only remove the moving field, but not the target
5095
5096       RemoveField(oldx, oldy);
5097
5098       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5099
5100       TEST_DrawLevelField(oldx, oldy);
5101
5102       return;
5103     }
5104   }
5105   else if (element == EL_BLOCKED)
5106   {
5107     Blocked2Moving(x, y, &oldx, &oldy);
5108     if (!IS_MOVING(oldx, oldy))
5109       return;
5110   }
5111
5112   if (element == EL_BLOCKED &&
5113       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5114        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5115        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5116        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5117        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5118        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5119     next_element = get_next_element(Feld[oldx][oldy]);
5120
5121   RemoveField(oldx, oldy);
5122   RemoveField(newx, newy);
5123
5124   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5125
5126   if (next_element != EL_UNDEFINED)
5127     Feld[oldx][oldy] = next_element;
5128
5129   TEST_DrawLevelField(oldx, oldy);
5130   TEST_DrawLevelField(newx, newy);
5131 }
5132
5133 void DrawDynamite(int x, int y)
5134 {
5135   int sx = SCREENX(x), sy = SCREENY(y);
5136   int graphic = el2img(Feld[x][y]);
5137   int frame;
5138
5139   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5140     return;
5141
5142   if (IS_WALKABLE_INSIDE(Back[x][y]))
5143     return;
5144
5145   if (Back[x][y])
5146     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5147   else if (Store[x][y])
5148     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5149
5150   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5151
5152   if (Back[x][y] || Store[x][y])
5153     DrawGraphicThruMask(sx, sy, graphic, frame);
5154   else
5155     DrawGraphic(sx, sy, graphic, frame);
5156 }
5157
5158 static void CheckDynamite(int x, int y)
5159 {
5160   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5161   {
5162     MovDelay[x][y]--;
5163
5164     if (MovDelay[x][y] != 0)
5165     {
5166       DrawDynamite(x, y);
5167       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5168
5169       return;
5170     }
5171   }
5172
5173   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5174
5175   Bang(x, y);
5176 }
5177
5178 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5179 {
5180   boolean num_checked_players = 0;
5181   int i;
5182
5183   for (i = 0; i < MAX_PLAYERS; i++)
5184   {
5185     if (stored_player[i].active)
5186     {
5187       int sx = stored_player[i].jx;
5188       int sy = stored_player[i].jy;
5189
5190       if (num_checked_players == 0)
5191       {
5192         *sx1 = *sx2 = sx;
5193         *sy1 = *sy2 = sy;
5194       }
5195       else
5196       {
5197         *sx1 = MIN(*sx1, sx);
5198         *sy1 = MIN(*sy1, sy);
5199         *sx2 = MAX(*sx2, sx);
5200         *sy2 = MAX(*sy2, sy);
5201       }
5202
5203       num_checked_players++;
5204     }
5205   }
5206 }
5207
5208 static boolean checkIfAllPlayersFitToScreen_RND(void)
5209 {
5210   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5211
5212   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5213
5214   return (sx2 - sx1 < SCR_FIELDX &&
5215           sy2 - sy1 < SCR_FIELDY);
5216 }
5217
5218 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5219 {
5220   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5221
5222   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5223
5224   *sx = (sx1 + sx2) / 2;
5225   *sy = (sy1 + sy2) / 2;
5226 }
5227
5228 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5229                                boolean center_screen, boolean quick_relocation)
5230 {
5231   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5232   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5233   boolean no_delay = (tape.warp_forward);
5234   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5235   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5236   int new_scroll_x, new_scroll_y;
5237
5238   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5239   {
5240     // case 1: quick relocation inside visible screen (without scrolling)
5241
5242     RedrawPlayfield();
5243
5244     return;
5245   }
5246
5247   if (!level.shifted_relocation || center_screen)
5248   {
5249     // relocation _with_ centering of screen
5250
5251     new_scroll_x = SCROLL_POSITION_X(x);
5252     new_scroll_y = SCROLL_POSITION_Y(y);
5253   }
5254   else
5255   {
5256     // relocation _without_ centering of screen
5257
5258     int center_scroll_x = SCROLL_POSITION_X(old_x);
5259     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5260     int offset_x = x + (scroll_x - center_scroll_x);
5261     int offset_y = y + (scroll_y - center_scroll_y);
5262
5263     // for new screen position, apply previous offset to center position
5264     new_scroll_x = SCROLL_POSITION_X(offset_x);
5265     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5266   }
5267
5268   if (quick_relocation)
5269   {
5270     // case 2: quick relocation (redraw without visible scrolling)
5271
5272     scroll_x = new_scroll_x;
5273     scroll_y = new_scroll_y;
5274
5275     RedrawPlayfield();
5276
5277     return;
5278   }
5279
5280   // case 3: visible relocation (with scrolling to new position)
5281
5282   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5283
5284   SetVideoFrameDelay(wait_delay_value);
5285
5286   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5287   {
5288     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5289     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5290
5291     if (dx == 0 && dy == 0)             // no scrolling needed at all
5292       break;
5293
5294     scroll_x -= dx;
5295     scroll_y -= dy;
5296
5297     // set values for horizontal/vertical screen scrolling (half tile size)
5298     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5299     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5300     int pos_x = dx * TILEX / 2;
5301     int pos_y = dy * TILEY / 2;
5302     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5303     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5304
5305     ScrollLevel(dx, dy);
5306     DrawAllPlayers();
5307
5308     // scroll in two steps of half tile size to make things smoother
5309     BlitScreenToBitmapExt_RND(window, fx, fy);
5310
5311     // scroll second step to align at full tile size
5312     BlitScreenToBitmap(window);
5313   }
5314
5315   DrawAllPlayers();
5316   BackToFront();
5317
5318   SetVideoFrameDelay(frame_delay_value_old);
5319 }
5320
5321 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5322 {
5323   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5324   int player_nr = GET_PLAYER_NR(el_player);
5325   struct PlayerInfo *player = &stored_player[player_nr];
5326   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5327   boolean no_delay = (tape.warp_forward);
5328   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5329   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5330   int old_jx = player->jx;
5331   int old_jy = player->jy;
5332   int old_element = Feld[old_jx][old_jy];
5333   int element = Feld[jx][jy];
5334   boolean player_relocated = (old_jx != jx || old_jy != jy);
5335
5336   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5337   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5338   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5339   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5340   int leave_side_horiz = move_dir_horiz;
5341   int leave_side_vert  = move_dir_vert;
5342   int enter_side = enter_side_horiz | enter_side_vert;
5343   int leave_side = leave_side_horiz | leave_side_vert;
5344
5345   if (player->buried)           // do not reanimate dead player
5346     return;
5347
5348   if (!player_relocated)        // no need to relocate the player
5349     return;
5350
5351   if (IS_PLAYER(jx, jy))        // player already placed at new position
5352   {
5353     RemoveField(jx, jy);        // temporarily remove newly placed player
5354     DrawLevelField(jx, jy);
5355   }
5356
5357   if (player->present)
5358   {
5359     while (player->MovPos)
5360     {
5361       ScrollPlayer(player, SCROLL_GO_ON);
5362       ScrollScreen(NULL, SCROLL_GO_ON);
5363
5364       AdvanceFrameAndPlayerCounters(player->index_nr);
5365
5366       DrawPlayer(player);
5367
5368       BackToFront_WithFrameDelay(wait_delay_value);
5369     }
5370
5371     DrawPlayer(player);         // needed here only to cleanup last field
5372     DrawLevelField(player->jx, player->jy);     // remove player graphic
5373
5374     player->is_moving = FALSE;
5375   }
5376
5377   if (IS_CUSTOM_ELEMENT(old_element))
5378     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5379                                CE_LEFT_BY_PLAYER,
5380                                player->index_bit, leave_side);
5381
5382   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5383                                       CE_PLAYER_LEAVES_X,
5384                                       player->index_bit, leave_side);
5385
5386   Feld[jx][jy] = el_player;
5387   InitPlayerField(jx, jy, el_player, TRUE);
5388
5389   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5390      possible that the relocation target field did not contain a player element,
5391      but a walkable element, to which the new player was relocated -- in this
5392      case, restore that (already initialized!) element on the player field */
5393   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5394   {
5395     Feld[jx][jy] = element;     // restore previously existing element
5396   }
5397
5398   // only visually relocate centered player
5399   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5400                      FALSE, level.instant_relocation);
5401
5402   TestIfPlayerTouchesBadThing(jx, jy);
5403   TestIfPlayerTouchesCustomElement(jx, jy);
5404
5405   if (IS_CUSTOM_ELEMENT(element))
5406     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5407                                player->index_bit, enter_side);
5408
5409   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5410                                       player->index_bit, enter_side);
5411
5412   if (player->is_switching)
5413   {
5414     /* ensure that relocation while still switching an element does not cause
5415        a new element to be treated as also switched directly after relocation
5416        (this is important for teleporter switches that teleport the player to
5417        a place where another teleporter switch is in the same direction, which
5418        would then incorrectly be treated as immediately switched before the
5419        direction key that caused the switch was released) */
5420
5421     player->switch_x += jx - old_jx;
5422     player->switch_y += jy - old_jy;
5423   }
5424 }
5425
5426 static void Explode(int ex, int ey, int phase, int mode)
5427 {
5428   int x, y;
5429   int last_phase;
5430   int border_element;
5431
5432   // !!! eliminate this variable !!!
5433   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5434
5435   if (game.explosions_delayed)
5436   {
5437     ExplodeField[ex][ey] = mode;
5438     return;
5439   }
5440
5441   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5442   {
5443     int center_element = Feld[ex][ey];
5444     int artwork_element, explosion_element;     // set these values later
5445
5446     // remove things displayed in background while burning dynamite
5447     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5448       Back[ex][ey] = 0;
5449
5450     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5451     {
5452       // put moving element to center field (and let it explode there)
5453       center_element = MovingOrBlocked2Element(ex, ey);
5454       RemoveMovingField(ex, ey);
5455       Feld[ex][ey] = center_element;
5456     }
5457
5458     // now "center_element" is finally determined -- set related values now
5459     artwork_element = center_element;           // for custom player artwork
5460     explosion_element = center_element;         // for custom player artwork
5461
5462     if (IS_PLAYER(ex, ey))
5463     {
5464       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5465
5466       artwork_element = stored_player[player_nr].artwork_element;
5467
5468       if (level.use_explosion_element[player_nr])
5469       {
5470         explosion_element = level.explosion_element[player_nr];
5471         artwork_element = explosion_element;
5472       }
5473     }
5474
5475     if (mode == EX_TYPE_NORMAL ||
5476         mode == EX_TYPE_CENTER ||
5477         mode == EX_TYPE_CROSS)
5478       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5479
5480     last_phase = element_info[explosion_element].explosion_delay + 1;
5481
5482     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5483     {
5484       int xx = x - ex + 1;
5485       int yy = y - ey + 1;
5486       int element;
5487
5488       if (!IN_LEV_FIELD(x, y) ||
5489           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5490           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5491         continue;
5492
5493       element = Feld[x][y];
5494
5495       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5496       {
5497         element = MovingOrBlocked2Element(x, y);
5498
5499         if (!IS_EXPLOSION_PROOF(element))
5500           RemoveMovingField(x, y);
5501       }
5502
5503       // indestructible elements can only explode in center (but not flames)
5504       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5505                                            mode == EX_TYPE_BORDER)) ||
5506           element == EL_FLAMES)
5507         continue;
5508
5509       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5510          behaviour, for example when touching a yamyam that explodes to rocks
5511          with active deadly shield, a rock is created under the player !!! */
5512       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5513 #if 0
5514       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5515           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5516            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5517 #else
5518       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5519 #endif
5520       {
5521         if (IS_ACTIVE_BOMB(element))
5522         {
5523           // re-activate things under the bomb like gate or penguin
5524           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5525           Back[x][y] = 0;
5526         }
5527
5528         continue;
5529       }
5530
5531       // save walkable background elements while explosion on same tile
5532       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5533           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5534         Back[x][y] = element;
5535
5536       // ignite explodable elements reached by other explosion
5537       if (element == EL_EXPLOSION)
5538         element = Store2[x][y];
5539
5540       if (AmoebaNr[x][y] &&
5541           (element == EL_AMOEBA_FULL ||
5542            element == EL_BD_AMOEBA ||
5543            element == EL_AMOEBA_GROWING))
5544       {
5545         AmoebaCnt[AmoebaNr[x][y]]--;
5546         AmoebaCnt2[AmoebaNr[x][y]]--;
5547       }
5548
5549       RemoveField(x, y);
5550
5551       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5552       {
5553         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5554
5555         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5556
5557         if (PLAYERINFO(ex, ey)->use_murphy)
5558           Store[x][y] = EL_EMPTY;
5559       }
5560
5561       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5562       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5563       else if (ELEM_IS_PLAYER(center_element))
5564         Store[x][y] = EL_EMPTY;
5565       else if (center_element == EL_YAMYAM)
5566         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5567       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5568         Store[x][y] = element_info[center_element].content.e[xx][yy];
5569 #if 1
5570       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5571       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5572       // otherwise) -- FIX THIS !!!
5573       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5574         Store[x][y] = element_info[element].content.e[1][1];
5575 #else
5576       else if (!CAN_EXPLODE(element))
5577         Store[x][y] = element_info[element].content.e[1][1];
5578 #endif
5579       else
5580         Store[x][y] = EL_EMPTY;
5581
5582       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5583           center_element == EL_AMOEBA_TO_DIAMOND)
5584         Store2[x][y] = element;
5585
5586       Feld[x][y] = EL_EXPLOSION;
5587       GfxElement[x][y] = artwork_element;
5588
5589       ExplodePhase[x][y] = 1;
5590       ExplodeDelay[x][y] = last_phase;
5591
5592       Stop[x][y] = TRUE;
5593     }
5594
5595     if (center_element == EL_YAMYAM)
5596       game.yamyam_content_nr =
5597         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5598
5599     return;
5600   }
5601
5602   if (Stop[ex][ey])
5603     return;
5604
5605   x = ex;
5606   y = ey;
5607
5608   if (phase == 1)
5609     GfxFrame[x][y] = 0;         // restart explosion animation
5610
5611   last_phase = ExplodeDelay[x][y];
5612
5613   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5614
5615   // this can happen if the player leaves an explosion just in time
5616   if (GfxElement[x][y] == EL_UNDEFINED)
5617     GfxElement[x][y] = EL_EMPTY;
5618
5619   border_element = Store2[x][y];
5620   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5621     border_element = StorePlayer[x][y];
5622
5623   if (phase == element_info[border_element].ignition_delay ||
5624       phase == last_phase)
5625   {
5626     boolean border_explosion = FALSE;
5627
5628     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5629         !PLAYER_EXPLOSION_PROTECTED(x, y))
5630     {
5631       KillPlayerUnlessExplosionProtected(x, y);
5632       border_explosion = TRUE;
5633     }
5634     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5635     {
5636       Feld[x][y] = Store2[x][y];
5637       Store2[x][y] = 0;
5638       Bang(x, y);
5639       border_explosion = TRUE;
5640     }
5641     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5642     {
5643       AmoebeUmwandeln(x, y);
5644       Store2[x][y] = 0;
5645       border_explosion = TRUE;
5646     }
5647
5648     // if an element just explodes due to another explosion (chain-reaction),
5649     // do not immediately end the new explosion when it was the last frame of
5650     // the explosion (as it would be done in the following "if"-statement!)
5651     if (border_explosion && phase == last_phase)
5652       return;
5653   }
5654
5655   if (phase == last_phase)
5656   {
5657     int element;
5658
5659     element = Feld[x][y] = Store[x][y];
5660     Store[x][y] = Store2[x][y] = 0;
5661     GfxElement[x][y] = EL_UNDEFINED;
5662
5663     // player can escape from explosions and might therefore be still alive
5664     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5665         element <= EL_PLAYER_IS_EXPLODING_4)
5666     {
5667       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5668       int explosion_element = EL_PLAYER_1 + player_nr;
5669       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5670       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5671
5672       if (level.use_explosion_element[player_nr])
5673         explosion_element = level.explosion_element[player_nr];
5674
5675       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5676                     element_info[explosion_element].content.e[xx][yy]);
5677     }
5678
5679     // restore probably existing indestructible background element
5680     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5681       element = Feld[x][y] = Back[x][y];
5682     Back[x][y] = 0;
5683
5684     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5685     GfxDir[x][y] = MV_NONE;
5686     ChangeDelay[x][y] = 0;
5687     ChangePage[x][y] = -1;
5688
5689     CustomValue[x][y] = 0;
5690
5691     InitField_WithBug2(x, y, FALSE);
5692
5693     TEST_DrawLevelField(x, y);
5694
5695     TestIfElementTouchesCustomElement(x, y);
5696
5697     if (GFX_CRUMBLED(element))
5698       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5699
5700     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5701       StorePlayer[x][y] = 0;
5702
5703     if (ELEM_IS_PLAYER(element))
5704       RelocatePlayer(x, y, element);
5705   }
5706   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5707   {
5708     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5709     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5710
5711     if (phase == delay)
5712       TEST_DrawLevelFieldCrumbled(x, y);
5713
5714     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5715     {
5716       DrawLevelElement(x, y, Back[x][y]);
5717       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5718     }
5719     else if (IS_WALKABLE_UNDER(Back[x][y]))
5720     {
5721       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5722       DrawLevelElementThruMask(x, y, Back[x][y]);
5723     }
5724     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5725       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5726   }
5727 }
5728
5729 static void DynaExplode(int ex, int ey)
5730 {
5731   int i, j;
5732   int dynabomb_element = Feld[ex][ey];
5733   int dynabomb_size = 1;
5734   boolean dynabomb_xl = FALSE;
5735   struct PlayerInfo *player;
5736   static int xy[4][2] =
5737   {
5738     { 0, -1 },
5739     { -1, 0 },
5740     { +1, 0 },
5741     { 0, +1 }
5742   };
5743
5744   if (IS_ACTIVE_BOMB(dynabomb_element))
5745   {
5746     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5747     dynabomb_size = player->dynabomb_size;
5748     dynabomb_xl = player->dynabomb_xl;
5749     player->dynabombs_left++;
5750   }
5751
5752   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5753
5754   for (i = 0; i < NUM_DIRECTIONS; i++)
5755   {
5756     for (j = 1; j <= dynabomb_size; j++)
5757     {
5758       int x = ex + j * xy[i][0];
5759       int y = ey + j * xy[i][1];
5760       int element;
5761
5762       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5763         break;
5764
5765       element = Feld[x][y];
5766
5767       // do not restart explosions of fields with active bombs
5768       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5769         continue;
5770
5771       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5772
5773       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5774           !IS_DIGGABLE(element) && !dynabomb_xl)
5775         break;
5776     }
5777   }
5778 }
5779
5780 void Bang(int x, int y)
5781 {
5782   int element = MovingOrBlocked2Element(x, y);
5783   int explosion_type = EX_TYPE_NORMAL;
5784
5785   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5786   {
5787     struct PlayerInfo *player = PLAYERINFO(x, y);
5788
5789     element = Feld[x][y] = player->initial_element;
5790
5791     if (level.use_explosion_element[player->index_nr])
5792     {
5793       int explosion_element = level.explosion_element[player->index_nr];
5794
5795       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5796         explosion_type = EX_TYPE_CROSS;
5797       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5798         explosion_type = EX_TYPE_CENTER;
5799     }
5800   }
5801
5802   switch (element)
5803   {
5804     case EL_BUG:
5805     case EL_SPACESHIP:
5806     case EL_BD_BUTTERFLY:
5807     case EL_BD_FIREFLY:
5808     case EL_YAMYAM:
5809     case EL_DARK_YAMYAM:
5810     case EL_ROBOT:
5811     case EL_PACMAN:
5812     case EL_MOLE:
5813       RaiseScoreElement(element);
5814       break;
5815
5816     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5817     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5818     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5819     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5820     case EL_DYNABOMB_INCREASE_NUMBER:
5821     case EL_DYNABOMB_INCREASE_SIZE:
5822     case EL_DYNABOMB_INCREASE_POWER:
5823       explosion_type = EX_TYPE_DYNA;
5824       break;
5825
5826     case EL_DC_LANDMINE:
5827       explosion_type = EX_TYPE_CENTER;
5828       break;
5829
5830     case EL_PENGUIN:
5831     case EL_LAMP:
5832     case EL_LAMP_ACTIVE:
5833     case EL_AMOEBA_TO_DIAMOND:
5834       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5835         explosion_type = EX_TYPE_CENTER;
5836       break;
5837
5838     default:
5839       if (element_info[element].explosion_type == EXPLODES_CROSS)
5840         explosion_type = EX_TYPE_CROSS;
5841       else if (element_info[element].explosion_type == EXPLODES_1X1)
5842         explosion_type = EX_TYPE_CENTER;
5843       break;
5844   }
5845
5846   if (explosion_type == EX_TYPE_DYNA)
5847     DynaExplode(x, y);
5848   else
5849     Explode(x, y, EX_PHASE_START, explosion_type);
5850
5851   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5852 }
5853
5854 static void SplashAcid(int x, int y)
5855 {
5856   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5857       (!IN_LEV_FIELD(x - 1, y - 2) ||
5858        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5859     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5860
5861   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5862       (!IN_LEV_FIELD(x + 1, y - 2) ||
5863        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5864     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5865
5866   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5867 }
5868
5869 static void InitBeltMovement(void)
5870 {
5871   static int belt_base_element[4] =
5872   {
5873     EL_CONVEYOR_BELT_1_LEFT,
5874     EL_CONVEYOR_BELT_2_LEFT,
5875     EL_CONVEYOR_BELT_3_LEFT,
5876     EL_CONVEYOR_BELT_4_LEFT
5877   };
5878   static int belt_base_active_element[4] =
5879   {
5880     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5881     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5882     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5883     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5884   };
5885
5886   int x, y, i, j;
5887
5888   // set frame order for belt animation graphic according to belt direction
5889   for (i = 0; i < NUM_BELTS; i++)
5890   {
5891     int belt_nr = i;
5892
5893     for (j = 0; j < NUM_BELT_PARTS; j++)
5894     {
5895       int element = belt_base_active_element[belt_nr] + j;
5896       int graphic_1 = el2img(element);
5897       int graphic_2 = el2panelimg(element);
5898
5899       if (game.belt_dir[i] == MV_LEFT)
5900       {
5901         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5902         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5903       }
5904       else
5905       {
5906         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5907         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5908       }
5909     }
5910   }
5911
5912   SCAN_PLAYFIELD(x, y)
5913   {
5914     int element = Feld[x][y];
5915
5916     for (i = 0; i < NUM_BELTS; i++)
5917     {
5918       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5919       {
5920         int e_belt_nr = getBeltNrFromBeltElement(element);
5921         int belt_nr = i;
5922
5923         if (e_belt_nr == belt_nr)
5924         {
5925           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5926
5927           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5928         }
5929       }
5930     }
5931   }
5932 }
5933
5934 static void ToggleBeltSwitch(int x, int y)
5935 {
5936   static int belt_base_element[4] =
5937   {
5938     EL_CONVEYOR_BELT_1_LEFT,
5939     EL_CONVEYOR_BELT_2_LEFT,
5940     EL_CONVEYOR_BELT_3_LEFT,
5941     EL_CONVEYOR_BELT_4_LEFT
5942   };
5943   static int belt_base_active_element[4] =
5944   {
5945     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5946     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5947     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5948     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5949   };
5950   static int belt_base_switch_element[4] =
5951   {
5952     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5953     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5954     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5955     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5956   };
5957   static int belt_move_dir[4] =
5958   {
5959     MV_LEFT,
5960     MV_NONE,
5961     MV_RIGHT,
5962     MV_NONE,
5963   };
5964
5965   int element = Feld[x][y];
5966   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5967   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5968   int belt_dir = belt_move_dir[belt_dir_nr];
5969   int xx, yy, i;
5970
5971   if (!IS_BELT_SWITCH(element))
5972     return;
5973
5974   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5975   game.belt_dir[belt_nr] = belt_dir;
5976
5977   if (belt_dir_nr == 3)
5978     belt_dir_nr = 1;
5979
5980   // set frame order for belt animation graphic according to belt direction
5981   for (i = 0; i < NUM_BELT_PARTS; i++)
5982   {
5983     int element = belt_base_active_element[belt_nr] + i;
5984     int graphic_1 = el2img(element);
5985     int graphic_2 = el2panelimg(element);
5986
5987     if (belt_dir == MV_LEFT)
5988     {
5989       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5990       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5991     }
5992     else
5993     {
5994       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5995       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5996     }
5997   }
5998
5999   SCAN_PLAYFIELD(xx, yy)
6000   {
6001     int element = Feld[xx][yy];
6002
6003     if (IS_BELT_SWITCH(element))
6004     {
6005       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6006
6007       if (e_belt_nr == belt_nr)
6008       {
6009         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6010         TEST_DrawLevelField(xx, yy);
6011       }
6012     }
6013     else if (IS_BELT(element) && belt_dir != MV_NONE)
6014     {
6015       int e_belt_nr = getBeltNrFromBeltElement(element);
6016
6017       if (e_belt_nr == belt_nr)
6018       {
6019         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6020
6021         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6022         TEST_DrawLevelField(xx, yy);
6023       }
6024     }
6025     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6026     {
6027       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6028
6029       if (e_belt_nr == belt_nr)
6030       {
6031         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6032
6033         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6034         TEST_DrawLevelField(xx, yy);
6035       }
6036     }
6037   }
6038 }
6039
6040 static void ToggleSwitchgateSwitch(int x, int y)
6041 {
6042   int xx, yy;
6043
6044   game.switchgate_pos = !game.switchgate_pos;
6045
6046   SCAN_PLAYFIELD(xx, yy)
6047   {
6048     int element = Feld[xx][yy];
6049
6050     if (element == EL_SWITCHGATE_SWITCH_UP)
6051     {
6052       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6053       TEST_DrawLevelField(xx, yy);
6054     }
6055     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6056     {
6057       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6058       TEST_DrawLevelField(xx, yy);
6059     }
6060     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6061     {
6062       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6063       TEST_DrawLevelField(xx, yy);
6064     }
6065     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6066     {
6067       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6068       TEST_DrawLevelField(xx, yy);
6069     }
6070     else if (element == EL_SWITCHGATE_OPEN ||
6071              element == EL_SWITCHGATE_OPENING)
6072     {
6073       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6074
6075       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6076     }
6077     else if (element == EL_SWITCHGATE_CLOSED ||
6078              element == EL_SWITCHGATE_CLOSING)
6079     {
6080       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6081
6082       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6083     }
6084   }
6085 }
6086
6087 static int getInvisibleActiveFromInvisibleElement(int element)
6088 {
6089   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6090           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6091           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6092           element);
6093 }
6094
6095 static int getInvisibleFromInvisibleActiveElement(int element)
6096 {
6097   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6098           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6099           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6100           element);
6101 }
6102
6103 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6104 {
6105   int x, y;
6106
6107   SCAN_PLAYFIELD(x, y)
6108   {
6109     int element = Feld[x][y];
6110
6111     if (element == EL_LIGHT_SWITCH &&
6112         game.light_time_left > 0)
6113     {
6114       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6115       TEST_DrawLevelField(x, y);
6116     }
6117     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6118              game.light_time_left == 0)
6119     {
6120       Feld[x][y] = EL_LIGHT_SWITCH;
6121       TEST_DrawLevelField(x, y);
6122     }
6123     else if (element == EL_EMC_DRIPPER &&
6124              game.light_time_left > 0)
6125     {
6126       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6127       TEST_DrawLevelField(x, y);
6128     }
6129     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6130              game.light_time_left == 0)
6131     {
6132       Feld[x][y] = EL_EMC_DRIPPER;
6133       TEST_DrawLevelField(x, y);
6134     }
6135     else if (element == EL_INVISIBLE_STEELWALL ||
6136              element == EL_INVISIBLE_WALL ||
6137              element == EL_INVISIBLE_SAND)
6138     {
6139       if (game.light_time_left > 0)
6140         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6141
6142       TEST_DrawLevelField(x, y);
6143
6144       // uncrumble neighbour fields, if needed
6145       if (element == EL_INVISIBLE_SAND)
6146         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6147     }
6148     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6149              element == EL_INVISIBLE_WALL_ACTIVE ||
6150              element == EL_INVISIBLE_SAND_ACTIVE)
6151     {
6152       if (game.light_time_left == 0)
6153         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6154
6155       TEST_DrawLevelField(x, y);
6156
6157       // re-crumble neighbour fields, if needed
6158       if (element == EL_INVISIBLE_SAND)
6159         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6160     }
6161   }
6162 }
6163
6164 static void RedrawAllInvisibleElementsForLenses(void)
6165 {
6166   int x, y;
6167
6168   SCAN_PLAYFIELD(x, y)
6169   {
6170     int element = Feld[x][y];
6171
6172     if (element == EL_EMC_DRIPPER &&
6173         game.lenses_time_left > 0)
6174     {
6175       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6176       TEST_DrawLevelField(x, y);
6177     }
6178     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6179              game.lenses_time_left == 0)
6180     {
6181       Feld[x][y] = EL_EMC_DRIPPER;
6182       TEST_DrawLevelField(x, y);
6183     }
6184     else if (element == EL_INVISIBLE_STEELWALL ||
6185              element == EL_INVISIBLE_WALL ||
6186              element == EL_INVISIBLE_SAND)
6187     {
6188       if (game.lenses_time_left > 0)
6189         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6190
6191       TEST_DrawLevelField(x, y);
6192
6193       // uncrumble neighbour fields, if needed
6194       if (element == EL_INVISIBLE_SAND)
6195         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6196     }
6197     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6198              element == EL_INVISIBLE_WALL_ACTIVE ||
6199              element == EL_INVISIBLE_SAND_ACTIVE)
6200     {
6201       if (game.lenses_time_left == 0)
6202         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6203
6204       TEST_DrawLevelField(x, y);
6205
6206       // re-crumble neighbour fields, if needed
6207       if (element == EL_INVISIBLE_SAND)
6208         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6209     }
6210   }
6211 }
6212
6213 static void RedrawAllInvisibleElementsForMagnifier(void)
6214 {
6215   int x, y;
6216
6217   SCAN_PLAYFIELD(x, y)
6218   {
6219     int element = Feld[x][y];
6220
6221     if (element == EL_EMC_FAKE_GRASS &&
6222         game.magnify_time_left > 0)
6223     {
6224       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6225       TEST_DrawLevelField(x, y);
6226     }
6227     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6228              game.magnify_time_left == 0)
6229     {
6230       Feld[x][y] = EL_EMC_FAKE_GRASS;
6231       TEST_DrawLevelField(x, y);
6232     }
6233     else if (IS_GATE_GRAY(element) &&
6234              game.magnify_time_left > 0)
6235     {
6236       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6237                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6238                     IS_EM_GATE_GRAY(element) ?
6239                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6240                     IS_EMC_GATE_GRAY(element) ?
6241                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6242                     IS_DC_GATE_GRAY(element) ?
6243                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6244                     element);
6245       TEST_DrawLevelField(x, y);
6246     }
6247     else if (IS_GATE_GRAY_ACTIVE(element) &&
6248              game.magnify_time_left == 0)
6249     {
6250       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6251                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6252                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6253                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6254                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6255                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6256                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6257                     EL_DC_GATE_WHITE_GRAY :
6258                     element);
6259       TEST_DrawLevelField(x, y);
6260     }
6261   }
6262 }
6263
6264 static void ToggleLightSwitch(int x, int y)
6265 {
6266   int element = Feld[x][y];
6267
6268   game.light_time_left =
6269     (element == EL_LIGHT_SWITCH ?
6270      level.time_light * FRAMES_PER_SECOND : 0);
6271
6272   RedrawAllLightSwitchesAndInvisibleElements();
6273 }
6274
6275 static void ActivateTimegateSwitch(int x, int y)
6276 {
6277   int xx, yy;
6278
6279   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6280
6281   SCAN_PLAYFIELD(xx, yy)
6282   {
6283     int element = Feld[xx][yy];
6284
6285     if (element == EL_TIMEGATE_CLOSED ||
6286         element == EL_TIMEGATE_CLOSING)
6287     {
6288       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6289       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6290     }
6291
6292     /*
6293     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6294     {
6295       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6296       TEST_DrawLevelField(xx, yy);
6297     }
6298     */
6299
6300   }
6301
6302   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6303                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6304 }
6305
6306 static void Impact(int x, int y)
6307 {
6308   boolean last_line = (y == lev_fieldy - 1);
6309   boolean object_hit = FALSE;
6310   boolean impact = (last_line || object_hit);
6311   int element = Feld[x][y];
6312   int smashed = EL_STEELWALL;
6313
6314   if (!last_line)       // check if element below was hit
6315   {
6316     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6317       return;
6318
6319     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6320                                          MovDir[x][y + 1] != MV_DOWN ||
6321                                          MovPos[x][y + 1] <= TILEY / 2));
6322
6323     // do not smash moving elements that left the smashed field in time
6324     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6325         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6326       object_hit = FALSE;
6327
6328 #if USE_QUICKSAND_IMPACT_BUGFIX
6329     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6330     {
6331       RemoveMovingField(x, y + 1);
6332       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6333       Feld[x][y + 2] = EL_ROCK;
6334       TEST_DrawLevelField(x, y + 2);
6335
6336       object_hit = TRUE;
6337     }
6338
6339     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6340     {
6341       RemoveMovingField(x, y + 1);
6342       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6343       Feld[x][y + 2] = EL_ROCK;
6344       TEST_DrawLevelField(x, y + 2);
6345
6346       object_hit = TRUE;
6347     }
6348 #endif
6349
6350     if (object_hit)
6351       smashed = MovingOrBlocked2Element(x, y + 1);
6352
6353     impact = (last_line || object_hit);
6354   }
6355
6356   if (!last_line && smashed == EL_ACID) // element falls into acid
6357   {
6358     SplashAcid(x, y + 1);
6359     return;
6360   }
6361
6362   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6363   // only reset graphic animation if graphic really changes after impact
6364   if (impact &&
6365       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6366   {
6367     ResetGfxAnimation(x, y);
6368     TEST_DrawLevelField(x, y);
6369   }
6370
6371   if (impact && CAN_EXPLODE_IMPACT(element))
6372   {
6373     Bang(x, y);
6374     return;
6375   }
6376   else if (impact && element == EL_PEARL &&
6377            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6378   {
6379     ResetGfxAnimation(x, y);
6380
6381     Feld[x][y] = EL_PEARL_BREAKING;
6382     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6383     return;
6384   }
6385   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6386   {
6387     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6388
6389     return;
6390   }
6391
6392   if (impact && element == EL_AMOEBA_DROP)
6393   {
6394     if (object_hit && IS_PLAYER(x, y + 1))
6395       KillPlayerUnlessEnemyProtected(x, y + 1);
6396     else if (object_hit && smashed == EL_PENGUIN)
6397       Bang(x, y + 1);
6398     else
6399     {
6400       Feld[x][y] = EL_AMOEBA_GROWING;
6401       Store[x][y] = EL_AMOEBA_WET;
6402
6403       ResetRandomAnimationValue(x, y);
6404     }
6405     return;
6406   }
6407
6408   if (object_hit)               // check which object was hit
6409   {
6410     if ((CAN_PASS_MAGIC_WALL(element) && 
6411          (smashed == EL_MAGIC_WALL ||
6412           smashed == EL_BD_MAGIC_WALL)) ||
6413         (CAN_PASS_DC_MAGIC_WALL(element) &&
6414          smashed == EL_DC_MAGIC_WALL))
6415     {
6416       int xx, yy;
6417       int activated_magic_wall =
6418         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6419          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6420          EL_DC_MAGIC_WALL_ACTIVE);
6421
6422       // activate magic wall / mill
6423       SCAN_PLAYFIELD(xx, yy)
6424       {
6425         if (Feld[xx][yy] == smashed)
6426           Feld[xx][yy] = activated_magic_wall;
6427       }
6428
6429       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6430       game.magic_wall_active = TRUE;
6431
6432       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6433                             SND_MAGIC_WALL_ACTIVATING :
6434                             smashed == EL_BD_MAGIC_WALL ?
6435                             SND_BD_MAGIC_WALL_ACTIVATING :
6436                             SND_DC_MAGIC_WALL_ACTIVATING));
6437     }
6438
6439     if (IS_PLAYER(x, y + 1))
6440     {
6441       if (CAN_SMASH_PLAYER(element))
6442       {
6443         KillPlayerUnlessEnemyProtected(x, y + 1);
6444         return;
6445       }
6446     }
6447     else if (smashed == EL_PENGUIN)
6448     {
6449       if (CAN_SMASH_PLAYER(element))
6450       {
6451         Bang(x, y + 1);
6452         return;
6453       }
6454     }
6455     else if (element == EL_BD_DIAMOND)
6456     {
6457       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6458       {
6459         Bang(x, y + 1);
6460         return;
6461       }
6462     }
6463     else if (((element == EL_SP_INFOTRON ||
6464                element == EL_SP_ZONK) &&
6465               (smashed == EL_SP_SNIKSNAK ||
6466                smashed == EL_SP_ELECTRON ||
6467                smashed == EL_SP_DISK_ORANGE)) ||
6468              (element == EL_SP_INFOTRON &&
6469               smashed == EL_SP_DISK_YELLOW))
6470     {
6471       Bang(x, y + 1);
6472       return;
6473     }
6474     else if (CAN_SMASH_EVERYTHING(element))
6475     {
6476       if (IS_CLASSIC_ENEMY(smashed) ||
6477           CAN_EXPLODE_SMASHED(smashed))
6478       {
6479         Bang(x, y + 1);
6480         return;
6481       }
6482       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6483       {
6484         if (smashed == EL_LAMP ||
6485             smashed == EL_LAMP_ACTIVE)
6486         {
6487           Bang(x, y + 1);
6488           return;
6489         }
6490         else if (smashed == EL_NUT)
6491         {
6492           Feld[x][y + 1] = EL_NUT_BREAKING;
6493           PlayLevelSound(x, y, SND_NUT_BREAKING);
6494           RaiseScoreElement(EL_NUT);
6495           return;
6496         }
6497         else if (smashed == EL_PEARL)
6498         {
6499           ResetGfxAnimation(x, y);
6500
6501           Feld[x][y + 1] = EL_PEARL_BREAKING;
6502           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6503           return;
6504         }
6505         else if (smashed == EL_DIAMOND)
6506         {
6507           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6508           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6509           return;
6510         }
6511         else if (IS_BELT_SWITCH(smashed))
6512         {
6513           ToggleBeltSwitch(x, y + 1);
6514         }
6515         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6516                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6517                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6518                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6519         {
6520           ToggleSwitchgateSwitch(x, y + 1);
6521         }
6522         else if (smashed == EL_LIGHT_SWITCH ||
6523                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6524         {
6525           ToggleLightSwitch(x, y + 1);
6526         }
6527         else
6528         {
6529           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6530
6531           CheckElementChangeBySide(x, y + 1, smashed, element,
6532                                    CE_SWITCHED, CH_SIDE_TOP);
6533           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6534                                             CH_SIDE_TOP);
6535         }
6536       }
6537       else
6538       {
6539         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6540       }
6541     }
6542   }
6543
6544   // play sound of magic wall / mill
6545   if (!last_line &&
6546       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6547        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6548        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6549   {
6550     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6551       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6552     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6553       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6554     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6555       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6556
6557     return;
6558   }
6559
6560   // play sound of object that hits the ground
6561   if (last_line || object_hit)
6562     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6563 }
6564
6565 static void TurnRoundExt(int x, int y)
6566 {
6567   static struct
6568   {
6569     int dx, dy;
6570   } move_xy[] =
6571   {
6572     {  0,  0 },
6573     { -1,  0 },
6574     { +1,  0 },
6575     {  0,  0 },
6576     {  0, -1 },
6577     {  0,  0 }, { 0, 0 }, { 0, 0 },
6578     {  0, +1 }
6579   };
6580   static struct
6581   {
6582     int left, right, back;
6583   } turn[] =
6584   {
6585     { 0,        0,              0        },
6586     { MV_DOWN,  MV_UP,          MV_RIGHT },
6587     { MV_UP,    MV_DOWN,        MV_LEFT  },
6588     { 0,        0,              0        },
6589     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6590     { 0,        0,              0        },
6591     { 0,        0,              0        },
6592     { 0,        0,              0        },
6593     { MV_RIGHT, MV_LEFT,        MV_UP    }
6594   };
6595
6596   int element = Feld[x][y];
6597   int move_pattern = element_info[element].move_pattern;
6598
6599   int old_move_dir = MovDir[x][y];
6600   int left_dir  = turn[old_move_dir].left;
6601   int right_dir = turn[old_move_dir].right;
6602   int back_dir  = turn[old_move_dir].back;
6603
6604   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6605   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6606   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6607   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6608
6609   int left_x  = x + left_dx,  left_y  = y + left_dy;
6610   int right_x = x + right_dx, right_y = y + right_dy;
6611   int move_x  = x + move_dx,  move_y  = y + move_dy;
6612
6613   int xx, yy;
6614
6615   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6616   {
6617     TestIfBadThingTouchesOtherBadThing(x, y);
6618
6619     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6620       MovDir[x][y] = right_dir;
6621     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6622       MovDir[x][y] = left_dir;
6623
6624     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6625       MovDelay[x][y] = 9;
6626     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6627       MovDelay[x][y] = 1;
6628   }
6629   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6630   {
6631     TestIfBadThingTouchesOtherBadThing(x, y);
6632
6633     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6634       MovDir[x][y] = left_dir;
6635     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6636       MovDir[x][y] = right_dir;
6637
6638     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6639       MovDelay[x][y] = 9;
6640     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6641       MovDelay[x][y] = 1;
6642   }
6643   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6644   {
6645     TestIfBadThingTouchesOtherBadThing(x, y);
6646
6647     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6648       MovDir[x][y] = left_dir;
6649     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6650       MovDir[x][y] = right_dir;
6651
6652     if (MovDir[x][y] != old_move_dir)
6653       MovDelay[x][y] = 9;
6654   }
6655   else if (element == EL_YAMYAM)
6656   {
6657     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6658     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6659
6660     if (can_turn_left && can_turn_right)
6661       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6662     else if (can_turn_left)
6663       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6664     else if (can_turn_right)
6665       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6666     else
6667       MovDir[x][y] = back_dir;
6668
6669     MovDelay[x][y] = 16 + 16 * RND(3);
6670   }
6671   else if (element == EL_DARK_YAMYAM)
6672   {
6673     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6674                                                          left_x, left_y);
6675     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6676                                                          right_x, right_y);
6677
6678     if (can_turn_left && can_turn_right)
6679       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6680     else if (can_turn_left)
6681       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6682     else if (can_turn_right)
6683       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6684     else
6685       MovDir[x][y] = back_dir;
6686
6687     MovDelay[x][y] = 16 + 16 * RND(3);
6688   }
6689   else if (element == EL_PACMAN)
6690   {
6691     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6692     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6693
6694     if (can_turn_left && can_turn_right)
6695       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6696     else if (can_turn_left)
6697       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6698     else if (can_turn_right)
6699       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6700     else
6701       MovDir[x][y] = back_dir;
6702
6703     MovDelay[x][y] = 6 + RND(40);
6704   }
6705   else if (element == EL_PIG)
6706   {
6707     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6708     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6709     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6710     boolean should_turn_left, should_turn_right, should_move_on;
6711     int rnd_value = 24;
6712     int rnd = RND(rnd_value);
6713
6714     should_turn_left = (can_turn_left &&
6715                         (!can_move_on ||
6716                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6717                                                    y + back_dy + left_dy)));
6718     should_turn_right = (can_turn_right &&
6719                          (!can_move_on ||
6720                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6721                                                     y + back_dy + right_dy)));
6722     should_move_on = (can_move_on &&
6723                       (!can_turn_left ||
6724                        !can_turn_right ||
6725                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6726                                                  y + move_dy + left_dy) ||
6727                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6728                                                  y + move_dy + right_dy)));
6729
6730     if (should_turn_left || should_turn_right || should_move_on)
6731     {
6732       if (should_turn_left && should_turn_right && should_move_on)
6733         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6734                         rnd < 2 * rnd_value / 3 ? right_dir :
6735                         old_move_dir);
6736       else if (should_turn_left && should_turn_right)
6737         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6738       else if (should_turn_left && should_move_on)
6739         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6740       else if (should_turn_right && should_move_on)
6741         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6742       else if (should_turn_left)
6743         MovDir[x][y] = left_dir;
6744       else if (should_turn_right)
6745         MovDir[x][y] = right_dir;
6746       else if (should_move_on)
6747         MovDir[x][y] = old_move_dir;
6748     }
6749     else if (can_move_on && rnd > rnd_value / 8)
6750       MovDir[x][y] = old_move_dir;
6751     else if (can_turn_left && can_turn_right)
6752       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6753     else if (can_turn_left && rnd > rnd_value / 8)
6754       MovDir[x][y] = left_dir;
6755     else if (can_turn_right && rnd > rnd_value/8)
6756       MovDir[x][y] = right_dir;
6757     else
6758       MovDir[x][y] = back_dir;
6759
6760     xx = x + move_xy[MovDir[x][y]].dx;
6761     yy = y + move_xy[MovDir[x][y]].dy;
6762
6763     if (!IN_LEV_FIELD(xx, yy) ||
6764         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6765       MovDir[x][y] = old_move_dir;
6766
6767     MovDelay[x][y] = 0;
6768   }
6769   else if (element == EL_DRAGON)
6770   {
6771     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6772     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6773     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6774     int rnd_value = 24;
6775     int rnd = RND(rnd_value);
6776
6777     if (can_move_on && rnd > rnd_value / 8)
6778       MovDir[x][y] = old_move_dir;
6779     else if (can_turn_left && can_turn_right)
6780       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6781     else if (can_turn_left && rnd > rnd_value / 8)
6782       MovDir[x][y] = left_dir;
6783     else if (can_turn_right && rnd > rnd_value / 8)
6784       MovDir[x][y] = right_dir;
6785     else
6786       MovDir[x][y] = back_dir;
6787
6788     xx = x + move_xy[MovDir[x][y]].dx;
6789     yy = y + move_xy[MovDir[x][y]].dy;
6790
6791     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6792       MovDir[x][y] = old_move_dir;
6793
6794     MovDelay[x][y] = 0;
6795   }
6796   else if (element == EL_MOLE)
6797   {
6798     boolean can_move_on =
6799       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6800                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6801                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6802     if (!can_move_on)
6803     {
6804       boolean can_turn_left =
6805         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6806                               IS_AMOEBOID(Feld[left_x][left_y])));
6807
6808       boolean can_turn_right =
6809         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6810                               IS_AMOEBOID(Feld[right_x][right_y])));
6811
6812       if (can_turn_left && can_turn_right)
6813         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6814       else if (can_turn_left)
6815         MovDir[x][y] = left_dir;
6816       else
6817         MovDir[x][y] = right_dir;
6818     }
6819
6820     if (MovDir[x][y] != old_move_dir)
6821       MovDelay[x][y] = 9;
6822   }
6823   else if (element == EL_BALLOON)
6824   {
6825     MovDir[x][y] = game.wind_direction;
6826     MovDelay[x][y] = 0;
6827   }
6828   else if (element == EL_SPRING)
6829   {
6830     if (MovDir[x][y] & MV_HORIZONTAL)
6831     {
6832       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6833           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6834       {
6835         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6836         ResetGfxAnimation(move_x, move_y);
6837         TEST_DrawLevelField(move_x, move_y);
6838
6839         MovDir[x][y] = back_dir;
6840       }
6841       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6842                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6843         MovDir[x][y] = MV_NONE;
6844     }
6845
6846     MovDelay[x][y] = 0;
6847   }
6848   else if (element == EL_ROBOT ||
6849            element == EL_SATELLITE ||
6850            element == EL_PENGUIN ||
6851            element == EL_EMC_ANDROID)
6852   {
6853     int attr_x = -1, attr_y = -1;
6854
6855     if (game.all_players_gone)
6856     {
6857       attr_x = game.exit_x;
6858       attr_y = game.exit_y;
6859     }
6860     else
6861     {
6862       int i;
6863
6864       for (i = 0; i < MAX_PLAYERS; i++)
6865       {
6866         struct PlayerInfo *player = &stored_player[i];
6867         int jx = player->jx, jy = player->jy;
6868
6869         if (!player->active)
6870           continue;
6871
6872         if (attr_x == -1 ||
6873             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6874         {
6875           attr_x = jx;
6876           attr_y = jy;
6877         }
6878       }
6879     }
6880
6881     if (element == EL_ROBOT &&
6882         game.robot_wheel_x >= 0 &&
6883         game.robot_wheel_y >= 0 &&
6884         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6885          game.engine_version < VERSION_IDENT(3,1,0,0)))
6886     {
6887       attr_x = game.robot_wheel_x;
6888       attr_y = game.robot_wheel_y;
6889     }
6890
6891     if (element == EL_PENGUIN)
6892     {
6893       int i;
6894       static int xy[4][2] =
6895       {
6896         { 0, -1 },
6897         { -1, 0 },
6898         { +1, 0 },
6899         { 0, +1 }
6900       };
6901
6902       for (i = 0; i < NUM_DIRECTIONS; i++)
6903       {
6904         int ex = x + xy[i][0];
6905         int ey = y + xy[i][1];
6906
6907         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6908                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6909                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6910                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6911         {
6912           attr_x = ex;
6913           attr_y = ey;
6914           break;
6915         }
6916       }
6917     }
6918
6919     MovDir[x][y] = MV_NONE;
6920     if (attr_x < x)
6921       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6922     else if (attr_x > x)
6923       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6924     if (attr_y < y)
6925       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6926     else if (attr_y > y)
6927       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6928
6929     if (element == EL_ROBOT)
6930     {
6931       int newx, newy;
6932
6933       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6934         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6935       Moving2Blocked(x, y, &newx, &newy);
6936
6937       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6938         MovDelay[x][y] = 8 + 8 * !RND(3);
6939       else
6940         MovDelay[x][y] = 16;
6941     }
6942     else if (element == EL_PENGUIN)
6943     {
6944       int newx, newy;
6945
6946       MovDelay[x][y] = 1;
6947
6948       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6949       {
6950         boolean first_horiz = RND(2);
6951         int new_move_dir = MovDir[x][y];
6952
6953         MovDir[x][y] =
6954           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6955         Moving2Blocked(x, y, &newx, &newy);
6956
6957         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6958           return;
6959
6960         MovDir[x][y] =
6961           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6962         Moving2Blocked(x, y, &newx, &newy);
6963
6964         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6965           return;
6966
6967         MovDir[x][y] = old_move_dir;
6968         return;
6969       }
6970     }
6971     else if (element == EL_SATELLITE)
6972     {
6973       int newx, newy;
6974
6975       MovDelay[x][y] = 1;
6976
6977       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6978       {
6979         boolean first_horiz = RND(2);
6980         int new_move_dir = MovDir[x][y];
6981
6982         MovDir[x][y] =
6983           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6984         Moving2Blocked(x, y, &newx, &newy);
6985
6986         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6987           return;
6988
6989         MovDir[x][y] =
6990           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6991         Moving2Blocked(x, y, &newx, &newy);
6992
6993         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6994           return;
6995
6996         MovDir[x][y] = old_move_dir;
6997         return;
6998       }
6999     }
7000     else if (element == EL_EMC_ANDROID)
7001     {
7002       static int check_pos[16] =
7003       {
7004         -1,             //  0 => (invalid)
7005         7,              //  1 => MV_LEFT
7006         3,              //  2 => MV_RIGHT
7007         -1,             //  3 => (invalid)
7008         1,              //  4 =>            MV_UP
7009         0,              //  5 => MV_LEFT  | MV_UP
7010         2,              //  6 => MV_RIGHT | MV_UP
7011         -1,             //  7 => (invalid)
7012         5,              //  8 =>            MV_DOWN
7013         6,              //  9 => MV_LEFT  | MV_DOWN
7014         4,              // 10 => MV_RIGHT | MV_DOWN
7015         -1,             // 11 => (invalid)
7016         -1,             // 12 => (invalid)
7017         -1,             // 13 => (invalid)
7018         -1,             // 14 => (invalid)
7019         -1,             // 15 => (invalid)
7020       };
7021       static struct
7022       {
7023         int dx, dy;
7024         int dir;
7025       } check_xy[8] =
7026       {
7027         { -1, -1,       MV_LEFT  | MV_UP   },
7028         {  0, -1,                  MV_UP   },
7029         { +1, -1,       MV_RIGHT | MV_UP   },
7030         { +1,  0,       MV_RIGHT           },
7031         { +1, +1,       MV_RIGHT | MV_DOWN },
7032         {  0, +1,                  MV_DOWN },
7033         { -1, +1,       MV_LEFT  | MV_DOWN },
7034         { -1,  0,       MV_LEFT            },
7035       };
7036       int start_pos, check_order;
7037       boolean can_clone = FALSE;
7038       int i;
7039
7040       // check if there is any free field around current position
7041       for (i = 0; i < 8; i++)
7042       {
7043         int newx = x + check_xy[i].dx;
7044         int newy = y + check_xy[i].dy;
7045
7046         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7047         {
7048           can_clone = TRUE;
7049
7050           break;
7051         }
7052       }
7053
7054       if (can_clone)            // randomly find an element to clone
7055       {
7056         can_clone = FALSE;
7057
7058         start_pos = check_pos[RND(8)];
7059         check_order = (RND(2) ? -1 : +1);
7060
7061         for (i = 0; i < 8; i++)
7062         {
7063           int pos_raw = start_pos + i * check_order;
7064           int pos = (pos_raw + 8) % 8;
7065           int newx = x + check_xy[pos].dx;
7066           int newy = y + check_xy[pos].dy;
7067
7068           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7069           {
7070             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7071             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7072
7073             Store[x][y] = Feld[newx][newy];
7074
7075             can_clone = TRUE;
7076
7077             break;
7078           }
7079         }
7080       }
7081
7082       if (can_clone)            // randomly find a direction to move
7083       {
7084         can_clone = FALSE;
7085
7086         start_pos = check_pos[RND(8)];
7087         check_order = (RND(2) ? -1 : +1);
7088
7089         for (i = 0; i < 8; i++)
7090         {
7091           int pos_raw = start_pos + i * check_order;
7092           int pos = (pos_raw + 8) % 8;
7093           int newx = x + check_xy[pos].dx;
7094           int newy = y + check_xy[pos].dy;
7095           int new_move_dir = check_xy[pos].dir;
7096
7097           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7098           {
7099             MovDir[x][y] = new_move_dir;
7100             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7101
7102             can_clone = TRUE;
7103
7104             break;
7105           }
7106         }
7107       }
7108
7109       if (can_clone)            // cloning and moving successful
7110         return;
7111
7112       // cannot clone -- try to move towards player
7113
7114       start_pos = check_pos[MovDir[x][y] & 0x0f];
7115       check_order = (RND(2) ? -1 : +1);
7116
7117       for (i = 0; i < 3; i++)
7118       {
7119         // first check start_pos, then previous/next or (next/previous) pos
7120         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7121         int pos = (pos_raw + 8) % 8;
7122         int newx = x + check_xy[pos].dx;
7123         int newy = y + check_xy[pos].dy;
7124         int new_move_dir = check_xy[pos].dir;
7125
7126         if (IS_PLAYER(newx, newy))
7127           break;
7128
7129         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7130         {
7131           MovDir[x][y] = new_move_dir;
7132           MovDelay[x][y] = level.android_move_time * 8 + 1;
7133
7134           break;
7135         }
7136       }
7137     }
7138   }
7139   else if (move_pattern == MV_TURNING_LEFT ||
7140            move_pattern == MV_TURNING_RIGHT ||
7141            move_pattern == MV_TURNING_LEFT_RIGHT ||
7142            move_pattern == MV_TURNING_RIGHT_LEFT ||
7143            move_pattern == MV_TURNING_RANDOM ||
7144            move_pattern == MV_ALL_DIRECTIONS)
7145   {
7146     boolean can_turn_left =
7147       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7148     boolean can_turn_right =
7149       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7150
7151     if (element_info[element].move_stepsize == 0)       // "not moving"
7152       return;
7153
7154     if (move_pattern == MV_TURNING_LEFT)
7155       MovDir[x][y] = left_dir;
7156     else if (move_pattern == MV_TURNING_RIGHT)
7157       MovDir[x][y] = right_dir;
7158     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7159       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7160     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7161       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7162     else if (move_pattern == MV_TURNING_RANDOM)
7163       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7164                       can_turn_right && !can_turn_left ? right_dir :
7165                       RND(2) ? left_dir : right_dir);
7166     else if (can_turn_left && can_turn_right)
7167       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7168     else if (can_turn_left)
7169       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7170     else if (can_turn_right)
7171       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7172     else
7173       MovDir[x][y] = back_dir;
7174
7175     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7176   }
7177   else if (move_pattern == MV_HORIZONTAL ||
7178            move_pattern == MV_VERTICAL)
7179   {
7180     if (move_pattern & old_move_dir)
7181       MovDir[x][y] = back_dir;
7182     else if (move_pattern == MV_HORIZONTAL)
7183       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7184     else if (move_pattern == MV_VERTICAL)
7185       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7186
7187     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7188   }
7189   else if (move_pattern & MV_ANY_DIRECTION)
7190   {
7191     MovDir[x][y] = move_pattern;
7192     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7193   }
7194   else if (move_pattern & MV_WIND_DIRECTION)
7195   {
7196     MovDir[x][y] = game.wind_direction;
7197     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7198   }
7199   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7200   {
7201     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7202       MovDir[x][y] = left_dir;
7203     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7204       MovDir[x][y] = right_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_ALONG_RIGHT_SIDE)
7210   {
7211     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7212       MovDir[x][y] = right_dir;
7213     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7214       MovDir[x][y] = left_dir;
7215
7216     if (MovDir[x][y] != old_move_dir)
7217       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7218   }
7219   else if (move_pattern == MV_TOWARDS_PLAYER ||
7220            move_pattern == MV_AWAY_FROM_PLAYER)
7221   {
7222     int attr_x = -1, attr_y = -1;
7223     int newx, newy;
7224     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7225
7226     if (game.all_players_gone)
7227     {
7228       attr_x = game.exit_x;
7229       attr_y = game.exit_y;
7230     }
7231     else
7232     {
7233       int i;
7234
7235       for (i = 0; i < MAX_PLAYERS; i++)
7236       {
7237         struct PlayerInfo *player = &stored_player[i];
7238         int jx = player->jx, jy = player->jy;
7239
7240         if (!player->active)
7241           continue;
7242
7243         if (attr_x == -1 ||
7244             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7245         {
7246           attr_x = jx;
7247           attr_y = jy;
7248         }
7249       }
7250     }
7251
7252     MovDir[x][y] = MV_NONE;
7253     if (attr_x < x)
7254       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7255     else if (attr_x > x)
7256       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7257     if (attr_y < y)
7258       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7259     else if (attr_y > y)
7260       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7261
7262     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7263
7264     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7265     {
7266       boolean first_horiz = RND(2);
7267       int new_move_dir = MovDir[x][y];
7268
7269       if (element_info[element].move_stepsize == 0)     // "not moving"
7270       {
7271         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7272         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7273
7274         return;
7275       }
7276
7277       MovDir[x][y] =
7278         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7279       Moving2Blocked(x, y, &newx, &newy);
7280
7281       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7282         return;
7283
7284       MovDir[x][y] =
7285         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7286       Moving2Blocked(x, y, &newx, &newy);
7287
7288       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7289         return;
7290
7291       MovDir[x][y] = old_move_dir;
7292     }
7293   }
7294   else if (move_pattern == MV_WHEN_PUSHED ||
7295            move_pattern == MV_WHEN_DROPPED)
7296   {
7297     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7298       MovDir[x][y] = MV_NONE;
7299
7300     MovDelay[x][y] = 0;
7301   }
7302   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7303   {
7304     static int test_xy[7][2] =
7305     {
7306       { 0, -1 },
7307       { -1, 0 },
7308       { +1, 0 },
7309       { 0, +1 },
7310       { 0, -1 },
7311       { -1, 0 },
7312       { +1, 0 },
7313     };
7314     static int test_dir[7] =
7315     {
7316       MV_UP,
7317       MV_LEFT,
7318       MV_RIGHT,
7319       MV_DOWN,
7320       MV_UP,
7321       MV_LEFT,
7322       MV_RIGHT,
7323     };
7324     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7325     int move_preference = -1000000;     // start with very low preference
7326     int new_move_dir = MV_NONE;
7327     int start_test = RND(4);
7328     int i;
7329
7330     for (i = 0; i < NUM_DIRECTIONS; i++)
7331     {
7332       int move_dir = test_dir[start_test + i];
7333       int move_dir_preference;
7334
7335       xx = x + test_xy[start_test + i][0];
7336       yy = y + test_xy[start_test + i][1];
7337
7338       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7339           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7340       {
7341         new_move_dir = move_dir;
7342
7343         break;
7344       }
7345
7346       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7347         continue;
7348
7349       move_dir_preference = -1 * RunnerVisit[xx][yy];
7350       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7351         move_dir_preference = PlayerVisit[xx][yy];
7352
7353       if (move_dir_preference > move_preference)
7354       {
7355         // prefer field that has not been visited for the longest time
7356         move_preference = move_dir_preference;
7357         new_move_dir = move_dir;
7358       }
7359       else if (move_dir_preference == move_preference &&
7360                move_dir == old_move_dir)
7361       {
7362         // prefer last direction when all directions are preferred equally
7363         move_preference = move_dir_preference;
7364         new_move_dir = move_dir;
7365       }
7366     }
7367
7368     MovDir[x][y] = new_move_dir;
7369     if (old_move_dir != new_move_dir)
7370       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7371   }
7372 }
7373
7374 static void TurnRound(int x, int y)
7375 {
7376   int direction = MovDir[x][y];
7377
7378   TurnRoundExt(x, y);
7379
7380   GfxDir[x][y] = MovDir[x][y];
7381
7382   if (direction != MovDir[x][y])
7383     GfxFrame[x][y] = 0;
7384
7385   if (MovDelay[x][y])
7386     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7387
7388   ResetGfxFrame(x, y);
7389 }
7390
7391 static boolean JustBeingPushed(int x, int y)
7392 {
7393   int i;
7394
7395   for (i = 0; i < MAX_PLAYERS; i++)
7396   {
7397     struct PlayerInfo *player = &stored_player[i];
7398
7399     if (player->active && player->is_pushing && player->MovPos)
7400     {
7401       int next_jx = player->jx + (player->jx - player->last_jx);
7402       int next_jy = player->jy + (player->jy - player->last_jy);
7403
7404       if (x == next_jx && y == next_jy)
7405         return TRUE;
7406     }
7407   }
7408
7409   return FALSE;
7410 }
7411
7412 static void StartMoving(int x, int y)
7413 {
7414   boolean started_moving = FALSE;       // some elements can fall _and_ move
7415   int element = Feld[x][y];
7416
7417   if (Stop[x][y])
7418     return;
7419
7420   if (MovDelay[x][y] == 0)
7421     GfxAction[x][y] = ACTION_DEFAULT;
7422
7423   if (CAN_FALL(element) && y < lev_fieldy - 1)
7424   {
7425     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7426         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7427       if (JustBeingPushed(x, y))
7428         return;
7429
7430     if (element == EL_QUICKSAND_FULL)
7431     {
7432       if (IS_FREE(x, y + 1))
7433       {
7434         InitMovingField(x, y, MV_DOWN);
7435         started_moving = TRUE;
7436
7437         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7438 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7439         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7440           Store[x][y] = EL_ROCK;
7441 #else
7442         Store[x][y] = EL_ROCK;
7443 #endif
7444
7445         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7446       }
7447       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7448       {
7449         if (!MovDelay[x][y])
7450         {
7451           MovDelay[x][y] = TILEY + 1;
7452
7453           ResetGfxAnimation(x, y);
7454           ResetGfxAnimation(x, y + 1);
7455         }
7456
7457         if (MovDelay[x][y])
7458         {
7459           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7460           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7461
7462           MovDelay[x][y]--;
7463           if (MovDelay[x][y])
7464             return;
7465         }
7466
7467         Feld[x][y] = EL_QUICKSAND_EMPTY;
7468         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7469         Store[x][y + 1] = Store[x][y];
7470         Store[x][y] = 0;
7471
7472         PlayLevelSoundAction(x, y, ACTION_FILLING);
7473       }
7474       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7475       {
7476         if (!MovDelay[x][y])
7477         {
7478           MovDelay[x][y] = TILEY + 1;
7479
7480           ResetGfxAnimation(x, y);
7481           ResetGfxAnimation(x, y + 1);
7482         }
7483
7484         if (MovDelay[x][y])
7485         {
7486           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7487           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7488
7489           MovDelay[x][y]--;
7490           if (MovDelay[x][y])
7491             return;
7492         }
7493
7494         Feld[x][y] = EL_QUICKSAND_EMPTY;
7495         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7496         Store[x][y + 1] = Store[x][y];
7497         Store[x][y] = 0;
7498
7499         PlayLevelSoundAction(x, y, ACTION_FILLING);
7500       }
7501     }
7502     else if (element == EL_QUICKSAND_FAST_FULL)
7503     {
7504       if (IS_FREE(x, y + 1))
7505       {
7506         InitMovingField(x, y, MV_DOWN);
7507         started_moving = TRUE;
7508
7509         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7510 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7511         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7512           Store[x][y] = EL_ROCK;
7513 #else
7514         Store[x][y] = EL_ROCK;
7515 #endif
7516
7517         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7518       }
7519       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7520       {
7521         if (!MovDelay[x][y])
7522         {
7523           MovDelay[x][y] = TILEY + 1;
7524
7525           ResetGfxAnimation(x, y);
7526           ResetGfxAnimation(x, y + 1);
7527         }
7528
7529         if (MovDelay[x][y])
7530         {
7531           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7532           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7533
7534           MovDelay[x][y]--;
7535           if (MovDelay[x][y])
7536             return;
7537         }
7538
7539         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7540         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7541         Store[x][y + 1] = Store[x][y];
7542         Store[x][y] = 0;
7543
7544         PlayLevelSoundAction(x, y, ACTION_FILLING);
7545       }
7546       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7547       {
7548         if (!MovDelay[x][y])
7549         {
7550           MovDelay[x][y] = TILEY + 1;
7551
7552           ResetGfxAnimation(x, y);
7553           ResetGfxAnimation(x, y + 1);
7554         }
7555
7556         if (MovDelay[x][y])
7557         {
7558           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7559           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7560
7561           MovDelay[x][y]--;
7562           if (MovDelay[x][y])
7563             return;
7564         }
7565
7566         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7567         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7568         Store[x][y + 1] = Store[x][y];
7569         Store[x][y] = 0;
7570
7571         PlayLevelSoundAction(x, y, ACTION_FILLING);
7572       }
7573     }
7574     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7575              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7576     {
7577       InitMovingField(x, y, MV_DOWN);
7578       started_moving = TRUE;
7579
7580       Feld[x][y] = EL_QUICKSAND_FILLING;
7581       Store[x][y] = element;
7582
7583       PlayLevelSoundAction(x, y, ACTION_FILLING);
7584     }
7585     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7586              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7587     {
7588       InitMovingField(x, y, MV_DOWN);
7589       started_moving = TRUE;
7590
7591       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7592       Store[x][y] = element;
7593
7594       PlayLevelSoundAction(x, y, ACTION_FILLING);
7595     }
7596     else if (element == EL_MAGIC_WALL_FULL)
7597     {
7598       if (IS_FREE(x, y + 1))
7599       {
7600         InitMovingField(x, y, MV_DOWN);
7601         started_moving = TRUE;
7602
7603         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7604         Store[x][y] = EL_CHANGED(Store[x][y]);
7605       }
7606       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7607       {
7608         if (!MovDelay[x][y])
7609           MovDelay[x][y] = TILEY / 4 + 1;
7610
7611         if (MovDelay[x][y])
7612         {
7613           MovDelay[x][y]--;
7614           if (MovDelay[x][y])
7615             return;
7616         }
7617
7618         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7619         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7620         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7621         Store[x][y] = 0;
7622       }
7623     }
7624     else if (element == EL_BD_MAGIC_WALL_FULL)
7625     {
7626       if (IS_FREE(x, y + 1))
7627       {
7628         InitMovingField(x, y, MV_DOWN);
7629         started_moving = TRUE;
7630
7631         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7632         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7633       }
7634       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7635       {
7636         if (!MovDelay[x][y])
7637           MovDelay[x][y] = TILEY / 4 + 1;
7638
7639         if (MovDelay[x][y])
7640         {
7641           MovDelay[x][y]--;
7642           if (MovDelay[x][y])
7643             return;
7644         }
7645
7646         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7647         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7648         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7649         Store[x][y] = 0;
7650       }
7651     }
7652     else if (element == EL_DC_MAGIC_WALL_FULL)
7653     {
7654       if (IS_FREE(x, y + 1))
7655       {
7656         InitMovingField(x, y, MV_DOWN);
7657         started_moving = TRUE;
7658
7659         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7660         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7661       }
7662       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7663       {
7664         if (!MovDelay[x][y])
7665           MovDelay[x][y] = TILEY / 4 + 1;
7666
7667         if (MovDelay[x][y])
7668         {
7669           MovDelay[x][y]--;
7670           if (MovDelay[x][y])
7671             return;
7672         }
7673
7674         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7675         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7676         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7677         Store[x][y] = 0;
7678       }
7679     }
7680     else if ((CAN_PASS_MAGIC_WALL(element) &&
7681               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7682                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7683              (CAN_PASS_DC_MAGIC_WALL(element) &&
7684               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7685
7686     {
7687       InitMovingField(x, y, MV_DOWN);
7688       started_moving = TRUE;
7689
7690       Feld[x][y] =
7691         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7692          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7693          EL_DC_MAGIC_WALL_FILLING);
7694       Store[x][y] = element;
7695     }
7696     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7697     {
7698       SplashAcid(x, y + 1);
7699
7700       InitMovingField(x, y, MV_DOWN);
7701       started_moving = TRUE;
7702
7703       Store[x][y] = EL_ACID;
7704     }
7705     else if (
7706              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7707               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7708              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7709               CAN_FALL(element) && WasJustFalling[x][y] &&
7710               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7711
7712              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7713               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7714               (Feld[x][y + 1] == EL_BLOCKED)))
7715     {
7716       /* this is needed for a special case not covered by calling "Impact()"
7717          from "ContinueMoving()": if an element moves to a tile directly below
7718          another element which was just falling on that tile (which was empty
7719          in the previous frame), the falling element above would just stop
7720          instead of smashing the element below (in previous version, the above
7721          element was just checked for "moving" instead of "falling", resulting
7722          in incorrect smashes caused by horizontal movement of the above
7723          element; also, the case of the player being the element to smash was
7724          simply not covered here... :-/ ) */
7725
7726       CheckCollision[x][y] = 0;
7727       CheckImpact[x][y] = 0;
7728
7729       Impact(x, y);
7730     }
7731     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7732     {
7733       if (MovDir[x][y] == MV_NONE)
7734       {
7735         InitMovingField(x, y, MV_DOWN);
7736         started_moving = TRUE;
7737       }
7738     }
7739     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7740     {
7741       if (WasJustFalling[x][y]) // prevent animation from being restarted
7742         MovDir[x][y] = MV_DOWN;
7743
7744       InitMovingField(x, y, MV_DOWN);
7745       started_moving = TRUE;
7746     }
7747     else if (element == EL_AMOEBA_DROP)
7748     {
7749       Feld[x][y] = EL_AMOEBA_GROWING;
7750       Store[x][y] = EL_AMOEBA_WET;
7751     }
7752     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7753               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7754              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7755              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7756     {
7757       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7758                                 (IS_FREE(x - 1, y + 1) ||
7759                                  Feld[x - 1][y + 1] == EL_ACID));
7760       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7761                                 (IS_FREE(x + 1, y + 1) ||
7762                                  Feld[x + 1][y + 1] == EL_ACID));
7763       boolean can_fall_any  = (can_fall_left || can_fall_right);
7764       boolean can_fall_both = (can_fall_left && can_fall_right);
7765       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7766
7767       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7768       {
7769         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7770           can_fall_right = FALSE;
7771         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7772           can_fall_left = FALSE;
7773         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7774           can_fall_right = FALSE;
7775         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7776           can_fall_left = FALSE;
7777
7778         can_fall_any  = (can_fall_left || can_fall_right);
7779         can_fall_both = FALSE;
7780       }
7781
7782       if (can_fall_both)
7783       {
7784         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7785           can_fall_right = FALSE;       // slip down on left side
7786         else
7787           can_fall_left = !(can_fall_right = RND(2));
7788
7789         can_fall_both = FALSE;
7790       }
7791
7792       if (can_fall_any)
7793       {
7794         // if not determined otherwise, prefer left side for slipping down
7795         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7796         started_moving = TRUE;
7797       }
7798     }
7799     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7800     {
7801       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7802       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7803       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7804       int belt_dir = game.belt_dir[belt_nr];
7805
7806       if ((belt_dir == MV_LEFT  && left_is_free) ||
7807           (belt_dir == MV_RIGHT && right_is_free))
7808       {
7809         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7810
7811         InitMovingField(x, y, belt_dir);
7812         started_moving = TRUE;
7813
7814         Pushed[x][y] = TRUE;
7815         Pushed[nextx][y] = TRUE;
7816
7817         GfxAction[x][y] = ACTION_DEFAULT;
7818       }
7819       else
7820       {
7821         MovDir[x][y] = 0;       // if element was moving, stop it
7822       }
7823     }
7824   }
7825
7826   // not "else if" because of elements that can fall and move (EL_SPRING)
7827   if (CAN_MOVE(element) && !started_moving)
7828   {
7829     int move_pattern = element_info[element].move_pattern;
7830     int newx, newy;
7831
7832     Moving2Blocked(x, y, &newx, &newy);
7833
7834     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7835       return;
7836
7837     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7838         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7839     {
7840       WasJustMoving[x][y] = 0;
7841       CheckCollision[x][y] = 0;
7842
7843       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7844
7845       if (Feld[x][y] != element)        // element has changed
7846         return;
7847     }
7848
7849     if (!MovDelay[x][y])        // start new movement phase
7850     {
7851       // all objects that can change their move direction after each step
7852       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7853
7854       if (element != EL_YAMYAM &&
7855           element != EL_DARK_YAMYAM &&
7856           element != EL_PACMAN &&
7857           !(move_pattern & MV_ANY_DIRECTION) &&
7858           move_pattern != MV_TURNING_LEFT &&
7859           move_pattern != MV_TURNING_RIGHT &&
7860           move_pattern != MV_TURNING_LEFT_RIGHT &&
7861           move_pattern != MV_TURNING_RIGHT_LEFT &&
7862           move_pattern != MV_TURNING_RANDOM)
7863       {
7864         TurnRound(x, y);
7865
7866         if (MovDelay[x][y] && (element == EL_BUG ||
7867                                element == EL_SPACESHIP ||
7868                                element == EL_SP_SNIKSNAK ||
7869                                element == EL_SP_ELECTRON ||
7870                                element == EL_MOLE))
7871           TEST_DrawLevelField(x, y);
7872       }
7873     }
7874
7875     if (MovDelay[x][y])         // wait some time before next movement
7876     {
7877       MovDelay[x][y]--;
7878
7879       if (element == EL_ROBOT ||
7880           element == EL_YAMYAM ||
7881           element == EL_DARK_YAMYAM)
7882       {
7883         DrawLevelElementAnimationIfNeeded(x, y, element);
7884         PlayLevelSoundAction(x, y, ACTION_WAITING);
7885       }
7886       else if (element == EL_SP_ELECTRON)
7887         DrawLevelElementAnimationIfNeeded(x, y, element);
7888       else if (element == EL_DRAGON)
7889       {
7890         int i;
7891         int dir = MovDir[x][y];
7892         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7893         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7894         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7895                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7896                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7897                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7898         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7899
7900         GfxAction[x][y] = ACTION_ATTACKING;
7901
7902         if (IS_PLAYER(x, y))
7903           DrawPlayerField(x, y);
7904         else
7905           TEST_DrawLevelField(x, y);
7906
7907         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7908
7909         for (i = 1; i <= 3; i++)
7910         {
7911           int xx = x + i * dx;
7912           int yy = y + i * dy;
7913           int sx = SCREENX(xx);
7914           int sy = SCREENY(yy);
7915           int flame_graphic = graphic + (i - 1);
7916
7917           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7918             break;
7919
7920           if (MovDelay[x][y])
7921           {
7922             int flamed = MovingOrBlocked2Element(xx, yy);
7923
7924             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7925               Bang(xx, yy);
7926             else
7927               RemoveMovingField(xx, yy);
7928
7929             ChangeDelay[xx][yy] = 0;
7930
7931             Feld[xx][yy] = EL_FLAMES;
7932
7933             if (IN_SCR_FIELD(sx, sy))
7934             {
7935               TEST_DrawLevelFieldCrumbled(xx, yy);
7936               DrawGraphic(sx, sy, flame_graphic, frame);
7937             }
7938           }
7939           else
7940           {
7941             if (Feld[xx][yy] == EL_FLAMES)
7942               Feld[xx][yy] = EL_EMPTY;
7943             TEST_DrawLevelField(xx, yy);
7944           }
7945         }
7946       }
7947
7948       if (MovDelay[x][y])       // element still has to wait some time
7949       {
7950         PlayLevelSoundAction(x, y, ACTION_WAITING);
7951
7952         return;
7953       }
7954     }
7955
7956     // now make next step
7957
7958     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7959
7960     if (DONT_COLLIDE_WITH(element) &&
7961         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7962         !PLAYER_ENEMY_PROTECTED(newx, newy))
7963     {
7964       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7965
7966       return;
7967     }
7968
7969     else if (CAN_MOVE_INTO_ACID(element) &&
7970              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7971              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7972              (MovDir[x][y] == MV_DOWN ||
7973               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7974     {
7975       SplashAcid(newx, newy);
7976       Store[x][y] = EL_ACID;
7977     }
7978     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7979     {
7980       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7981           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7982           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7983           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7984       {
7985         RemoveField(x, y);
7986         TEST_DrawLevelField(x, y);
7987
7988         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7989         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7990           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7991
7992         game.friends_still_needed--;
7993         if (!game.friends_still_needed &&
7994             !game.GameOver &&
7995             game.all_players_gone)
7996           LevelSolved();
7997
7998         return;
7999       }
8000       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8001       {
8002         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8003           TEST_DrawLevelField(newx, newy);
8004         else
8005           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8006       }
8007       else if (!IS_FREE(newx, newy))
8008       {
8009         GfxAction[x][y] = ACTION_WAITING;
8010
8011         if (IS_PLAYER(x, y))
8012           DrawPlayerField(x, y);
8013         else
8014           TEST_DrawLevelField(x, y);
8015
8016         return;
8017       }
8018     }
8019     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8020     {
8021       if (IS_FOOD_PIG(Feld[newx][newy]))
8022       {
8023         if (IS_MOVING(newx, newy))
8024           RemoveMovingField(newx, newy);
8025         else
8026         {
8027           Feld[newx][newy] = EL_EMPTY;
8028           TEST_DrawLevelField(newx, newy);
8029         }
8030
8031         PlayLevelSound(x, y, SND_PIG_DIGGING);
8032       }
8033       else if (!IS_FREE(newx, newy))
8034       {
8035         if (IS_PLAYER(x, y))
8036           DrawPlayerField(x, y);
8037         else
8038           TEST_DrawLevelField(x, y);
8039
8040         return;
8041       }
8042     }
8043     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8044     {
8045       if (Store[x][y] != EL_EMPTY)
8046       {
8047         boolean can_clone = FALSE;
8048         int xx, yy;
8049
8050         // check if element to clone is still there
8051         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8052         {
8053           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8054           {
8055             can_clone = TRUE;
8056
8057             break;
8058           }
8059         }
8060
8061         // cannot clone or target field not free anymore -- do not clone
8062         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8063           Store[x][y] = EL_EMPTY;
8064       }
8065
8066       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8067       {
8068         if (IS_MV_DIAGONAL(MovDir[x][y]))
8069         {
8070           int diagonal_move_dir = MovDir[x][y];
8071           int stored = Store[x][y];
8072           int change_delay = 8;
8073           int graphic;
8074
8075           // android is moving diagonally
8076
8077           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8078
8079           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8080           GfxElement[x][y] = EL_EMC_ANDROID;
8081           GfxAction[x][y] = ACTION_SHRINKING;
8082           GfxDir[x][y] = diagonal_move_dir;
8083           ChangeDelay[x][y] = change_delay;
8084
8085           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8086                                    GfxDir[x][y]);
8087
8088           DrawLevelGraphicAnimation(x, y, graphic);
8089           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8090
8091           if (Feld[newx][newy] == EL_ACID)
8092           {
8093             SplashAcid(newx, newy);
8094
8095             return;
8096           }
8097
8098           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8099
8100           Store[newx][newy] = EL_EMC_ANDROID;
8101           GfxElement[newx][newy] = EL_EMC_ANDROID;
8102           GfxAction[newx][newy] = ACTION_GROWING;
8103           GfxDir[newx][newy] = diagonal_move_dir;
8104           ChangeDelay[newx][newy] = change_delay;
8105
8106           graphic = el_act_dir2img(GfxElement[newx][newy],
8107                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8108
8109           DrawLevelGraphicAnimation(newx, newy, graphic);
8110           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8111
8112           return;
8113         }
8114         else
8115         {
8116           Feld[newx][newy] = EL_EMPTY;
8117           TEST_DrawLevelField(newx, newy);
8118
8119           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8120         }
8121       }
8122       else if (!IS_FREE(newx, newy))
8123       {
8124         return;
8125       }
8126     }
8127     else if (IS_CUSTOM_ELEMENT(element) &&
8128              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8129     {
8130       if (!DigFieldByCE(newx, newy, element))
8131         return;
8132
8133       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8134       {
8135         RunnerVisit[x][y] = FrameCounter;
8136         PlayerVisit[x][y] /= 8;         // expire player visit path
8137       }
8138     }
8139     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8140     {
8141       if (!IS_FREE(newx, newy))
8142       {
8143         if (IS_PLAYER(x, y))
8144           DrawPlayerField(x, y);
8145         else
8146           TEST_DrawLevelField(x, y);
8147
8148         return;
8149       }
8150       else
8151       {
8152         boolean wanna_flame = !RND(10);
8153         int dx = newx - x, dy = newy - y;
8154         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8155         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8156         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8157                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8158         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8159                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8160
8161         if ((wanna_flame ||
8162              IS_CLASSIC_ENEMY(element1) ||
8163              IS_CLASSIC_ENEMY(element2)) &&
8164             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8165             element1 != EL_FLAMES && element2 != EL_FLAMES)
8166         {
8167           ResetGfxAnimation(x, y);
8168           GfxAction[x][y] = ACTION_ATTACKING;
8169
8170           if (IS_PLAYER(x, y))
8171             DrawPlayerField(x, y);
8172           else
8173             TEST_DrawLevelField(x, y);
8174
8175           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8176
8177           MovDelay[x][y] = 50;
8178
8179           Feld[newx][newy] = EL_FLAMES;
8180           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8181             Feld[newx1][newy1] = EL_FLAMES;
8182           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8183             Feld[newx2][newy2] = EL_FLAMES;
8184
8185           return;
8186         }
8187       }
8188     }
8189     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8190              Feld[newx][newy] == EL_DIAMOND)
8191     {
8192       if (IS_MOVING(newx, newy))
8193         RemoveMovingField(newx, newy);
8194       else
8195       {
8196         Feld[newx][newy] = EL_EMPTY;
8197         TEST_DrawLevelField(newx, newy);
8198       }
8199
8200       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8201     }
8202     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8203              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8204     {
8205       if (AmoebaNr[newx][newy])
8206       {
8207         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8208         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8209             Feld[newx][newy] == EL_BD_AMOEBA)
8210           AmoebaCnt[AmoebaNr[newx][newy]]--;
8211       }
8212
8213       if (IS_MOVING(newx, newy))
8214       {
8215         RemoveMovingField(newx, newy);
8216       }
8217       else
8218       {
8219         Feld[newx][newy] = EL_EMPTY;
8220         TEST_DrawLevelField(newx, newy);
8221       }
8222
8223       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8224     }
8225     else if ((element == EL_PACMAN || element == EL_MOLE)
8226              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8227     {
8228       if (AmoebaNr[newx][newy])
8229       {
8230         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8231         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8232             Feld[newx][newy] == EL_BD_AMOEBA)
8233           AmoebaCnt[AmoebaNr[newx][newy]]--;
8234       }
8235
8236       if (element == EL_MOLE)
8237       {
8238         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8239         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8240
8241         ResetGfxAnimation(x, y);
8242         GfxAction[x][y] = ACTION_DIGGING;
8243         TEST_DrawLevelField(x, y);
8244
8245         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8246
8247         return;                         // wait for shrinking amoeba
8248       }
8249       else      // element == EL_PACMAN
8250       {
8251         Feld[newx][newy] = EL_EMPTY;
8252         TEST_DrawLevelField(newx, newy);
8253         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8254       }
8255     }
8256     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8257              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8258               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8259     {
8260       // wait for shrinking amoeba to completely disappear
8261       return;
8262     }
8263     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8264     {
8265       // object was running against a wall
8266
8267       TurnRound(x, y);
8268
8269       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8270         DrawLevelElementAnimation(x, y, element);
8271
8272       if (DONT_TOUCH(element))
8273         TestIfBadThingTouchesPlayer(x, y);
8274
8275       return;
8276     }
8277
8278     InitMovingField(x, y, MovDir[x][y]);
8279
8280     PlayLevelSoundAction(x, y, ACTION_MOVING);
8281   }
8282
8283   if (MovDir[x][y])
8284     ContinueMoving(x, y);
8285 }
8286
8287 void ContinueMoving(int x, int y)
8288 {
8289   int element = Feld[x][y];
8290   struct ElementInfo *ei = &element_info[element];
8291   int direction = MovDir[x][y];
8292   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8293   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8294   int newx = x + dx, newy = y + dy;
8295   int stored = Store[x][y];
8296   int stored_new = Store[newx][newy];
8297   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8298   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8299   boolean last_line = (newy == lev_fieldy - 1);
8300
8301   MovPos[x][y] += getElementMoveStepsize(x, y);
8302
8303   if (pushed_by_player) // special case: moving object pushed by player
8304     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8305
8306   if (ABS(MovPos[x][y]) < TILEX)
8307   {
8308     TEST_DrawLevelField(x, y);
8309
8310     return;     // element is still moving
8311   }
8312
8313   // element reached destination field
8314
8315   Feld[x][y] = EL_EMPTY;
8316   Feld[newx][newy] = element;
8317   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8318
8319   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8320   {
8321     element = Feld[newx][newy] = EL_ACID;
8322   }
8323   else if (element == EL_MOLE)
8324   {
8325     Feld[x][y] = EL_SAND;
8326
8327     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8328   }
8329   else if (element == EL_QUICKSAND_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_EMPTYING)
8335   {
8336     Feld[x][y] = get_next_element(element);
8337     element = Feld[newx][newy] = Store[x][y];
8338   }
8339   else if (element == EL_QUICKSAND_FAST_FILLING)
8340   {
8341     element = Feld[newx][newy] = get_next_element(element);
8342     Store[newx][newy] = Store[x][y];
8343   }
8344   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8345   {
8346     Feld[x][y] = get_next_element(element);
8347     element = Feld[newx][newy] = Store[x][y];
8348   }
8349   else if (element == EL_MAGIC_WALL_FILLING)
8350   {
8351     element = Feld[newx][newy] = get_next_element(element);
8352     if (!game.magic_wall_active)
8353       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8354     Store[newx][newy] = Store[x][y];
8355   }
8356   else if (element == EL_MAGIC_WALL_EMPTYING)
8357   {
8358     Feld[x][y] = get_next_element(element);
8359     if (!game.magic_wall_active)
8360       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8361     element = Feld[newx][newy] = Store[x][y];
8362
8363     InitField(newx, newy, FALSE);
8364   }
8365   else if (element == EL_BD_MAGIC_WALL_FILLING)
8366   {
8367     element = Feld[newx][newy] = get_next_element(element);
8368     if (!game.magic_wall_active)
8369       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8370     Store[newx][newy] = Store[x][y];
8371   }
8372   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8373   {
8374     Feld[x][y] = get_next_element(element);
8375     if (!game.magic_wall_active)
8376       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8377     element = Feld[newx][newy] = Store[x][y];
8378
8379     InitField(newx, newy, FALSE);
8380   }
8381   else if (element == EL_DC_MAGIC_WALL_FILLING)
8382   {
8383     element = Feld[newx][newy] = get_next_element(element);
8384     if (!game.magic_wall_active)
8385       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8386     Store[newx][newy] = Store[x][y];
8387   }
8388   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8389   {
8390     Feld[x][y] = get_next_element(element);
8391     if (!game.magic_wall_active)
8392       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8393     element = Feld[newx][newy] = Store[x][y];
8394
8395     InitField(newx, newy, FALSE);
8396   }
8397   else if (element == EL_AMOEBA_DROPPING)
8398   {
8399     Feld[x][y] = get_next_element(element);
8400     element = Feld[newx][newy] = Store[x][y];
8401   }
8402   else if (element == EL_SOKOBAN_OBJECT)
8403   {
8404     if (Back[x][y])
8405       Feld[x][y] = Back[x][y];
8406
8407     if (Back[newx][newy])
8408       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8409
8410     Back[x][y] = Back[newx][newy] = 0;
8411   }
8412
8413   Store[x][y] = EL_EMPTY;
8414   MovPos[x][y] = 0;
8415   MovDir[x][y] = 0;
8416   MovDelay[x][y] = 0;
8417
8418   MovDelay[newx][newy] = 0;
8419
8420   if (CAN_CHANGE_OR_HAS_ACTION(element))
8421   {
8422     // copy element change control values to new field
8423     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8424     ChangePage[newx][newy]  = ChangePage[x][y];
8425     ChangeCount[newx][newy] = ChangeCount[x][y];
8426     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8427   }
8428
8429   CustomValue[newx][newy] = CustomValue[x][y];
8430
8431   ChangeDelay[x][y] = 0;
8432   ChangePage[x][y] = -1;
8433   ChangeCount[x][y] = 0;
8434   ChangeEvent[x][y] = -1;
8435
8436   CustomValue[x][y] = 0;
8437
8438   // copy animation control values to new field
8439   GfxFrame[newx][newy]  = GfxFrame[x][y];
8440   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8441   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8442   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8443
8444   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8445
8446   // some elements can leave other elements behind after moving
8447   if (ei->move_leave_element != EL_EMPTY &&
8448       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8449       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8450   {
8451     int move_leave_element = ei->move_leave_element;
8452
8453     // this makes it possible to leave the removed element again
8454     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8455       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8456
8457     Feld[x][y] = move_leave_element;
8458
8459     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8460       MovDir[x][y] = direction;
8461
8462     InitField(x, y, FALSE);
8463
8464     if (GFX_CRUMBLED(Feld[x][y]))
8465       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8466
8467     if (ELEM_IS_PLAYER(move_leave_element))
8468       RelocatePlayer(x, y, move_leave_element);
8469   }
8470
8471   // do this after checking for left-behind element
8472   ResetGfxAnimation(x, y);      // reset animation values for old field
8473
8474   if (!CAN_MOVE(element) ||
8475       (CAN_FALL(element) && direction == MV_DOWN &&
8476        (element == EL_SPRING ||
8477         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8478         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8479     GfxDir[x][y] = MovDir[newx][newy] = 0;
8480
8481   TEST_DrawLevelField(x, y);
8482   TEST_DrawLevelField(newx, newy);
8483
8484   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8485
8486   // prevent pushed element from moving on in pushed direction
8487   if (pushed_by_player && CAN_MOVE(element) &&
8488       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8489       !(element_info[element].move_pattern & direction))
8490     TurnRound(newx, newy);
8491
8492   // prevent elements on conveyor belt from moving on in last direction
8493   if (pushed_by_conveyor && CAN_FALL(element) &&
8494       direction & MV_HORIZONTAL)
8495     MovDir[newx][newy] = 0;
8496
8497   if (!pushed_by_player)
8498   {
8499     int nextx = newx + dx, nexty = newy + dy;
8500     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8501
8502     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8503
8504     if (CAN_FALL(element) && direction == MV_DOWN)
8505       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8506
8507     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8508       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8509
8510     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8511       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8512   }
8513
8514   if (DONT_TOUCH(element))      // object may be nasty to player or others
8515   {
8516     TestIfBadThingTouchesPlayer(newx, newy);
8517     TestIfBadThingTouchesFriend(newx, newy);
8518
8519     if (!IS_CUSTOM_ELEMENT(element))
8520       TestIfBadThingTouchesOtherBadThing(newx, newy);
8521   }
8522   else if (element == EL_PENGUIN)
8523     TestIfFriendTouchesBadThing(newx, newy);
8524
8525   if (DONT_GET_HIT_BY(element))
8526   {
8527     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8528   }
8529
8530   // give the player one last chance (one more frame) to move away
8531   if (CAN_FALL(element) && direction == MV_DOWN &&
8532       (last_line || (!IS_FREE(x, newy + 1) &&
8533                      (!IS_PLAYER(x, newy + 1) ||
8534                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8535     Impact(x, newy);
8536
8537   if (pushed_by_player && !game.use_change_when_pushing_bug)
8538   {
8539     int push_side = MV_DIR_OPPOSITE(direction);
8540     struct PlayerInfo *player = PLAYERINFO(x, y);
8541
8542     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8543                                player->index_bit, push_side);
8544     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8545                                         player->index_bit, push_side);
8546   }
8547
8548   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8549     MovDelay[newx][newy] = 1;
8550
8551   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8552
8553   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8554   TestIfElementHitsCustomElement(newx, newy, direction);
8555   TestIfPlayerTouchesCustomElement(newx, newy);
8556   TestIfElementTouchesCustomElement(newx, newy);
8557
8558   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8559       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8560     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8561                              MV_DIR_OPPOSITE(direction));
8562 }
8563
8564 int AmoebeNachbarNr(int ax, int ay)
8565 {
8566   int i;
8567   int element = Feld[ax][ay];
8568   int group_nr = 0;
8569   static int xy[4][2] =
8570   {
8571     { 0, -1 },
8572     { -1, 0 },
8573     { +1, 0 },
8574     { 0, +1 }
8575   };
8576
8577   for (i = 0; i < NUM_DIRECTIONS; i++)
8578   {
8579     int x = ax + xy[i][0];
8580     int y = ay + xy[i][1];
8581
8582     if (!IN_LEV_FIELD(x, y))
8583       continue;
8584
8585     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8586       group_nr = AmoebaNr[x][y];
8587   }
8588
8589   return group_nr;
8590 }
8591
8592 static void AmoebenVereinigen(int ax, int ay)
8593 {
8594   int i, x, y, xx, yy;
8595   int new_group_nr = AmoebaNr[ax][ay];
8596   static int xy[4][2] =
8597   {
8598     { 0, -1 },
8599     { -1, 0 },
8600     { +1, 0 },
8601     { 0, +1 }
8602   };
8603
8604   if (new_group_nr == 0)
8605     return;
8606
8607   for (i = 0; i < NUM_DIRECTIONS; i++)
8608   {
8609     x = ax + xy[i][0];
8610     y = ay + xy[i][1];
8611
8612     if (!IN_LEV_FIELD(x, y))
8613       continue;
8614
8615     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8616          Feld[x][y] == EL_BD_AMOEBA ||
8617          Feld[x][y] == EL_AMOEBA_DEAD) &&
8618         AmoebaNr[x][y] != new_group_nr)
8619     {
8620       int old_group_nr = AmoebaNr[x][y];
8621
8622       if (old_group_nr == 0)
8623         return;
8624
8625       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8626       AmoebaCnt[old_group_nr] = 0;
8627       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8628       AmoebaCnt2[old_group_nr] = 0;
8629
8630       SCAN_PLAYFIELD(xx, yy)
8631       {
8632         if (AmoebaNr[xx][yy] == old_group_nr)
8633           AmoebaNr[xx][yy] = new_group_nr;
8634       }
8635     }
8636   }
8637 }
8638
8639 void AmoebeUmwandeln(int ax, int ay)
8640 {
8641   int i, x, y;
8642
8643   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8644   {
8645     int group_nr = AmoebaNr[ax][ay];
8646
8647 #ifdef DEBUG
8648     if (group_nr == 0)
8649     {
8650       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8651       printf("AmoebeUmwandeln(): This should never happen!\n");
8652       return;
8653     }
8654 #endif
8655
8656     SCAN_PLAYFIELD(x, y)
8657     {
8658       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8659       {
8660         AmoebaNr[x][y] = 0;
8661         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8662       }
8663     }
8664
8665     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8666                             SND_AMOEBA_TURNING_TO_GEM :
8667                             SND_AMOEBA_TURNING_TO_ROCK));
8668     Bang(ax, ay);
8669   }
8670   else
8671   {
8672     static int xy[4][2] =
8673     {
8674       { 0, -1 },
8675       { -1, 0 },
8676       { +1, 0 },
8677       { 0, +1 }
8678     };
8679
8680     for (i = 0; i < NUM_DIRECTIONS; i++)
8681     {
8682       x = ax + xy[i][0];
8683       y = ay + xy[i][1];
8684
8685       if (!IN_LEV_FIELD(x, y))
8686         continue;
8687
8688       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8689       {
8690         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8691                               SND_AMOEBA_TURNING_TO_GEM :
8692                               SND_AMOEBA_TURNING_TO_ROCK));
8693         Bang(x, y);
8694       }
8695     }
8696   }
8697 }
8698
8699 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8700 {
8701   int x, y;
8702   int group_nr = AmoebaNr[ax][ay];
8703   boolean done = FALSE;
8704
8705 #ifdef DEBUG
8706   if (group_nr == 0)
8707   {
8708     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8709     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8710     return;
8711   }
8712 #endif
8713
8714   SCAN_PLAYFIELD(x, y)
8715   {
8716     if (AmoebaNr[x][y] == group_nr &&
8717         (Feld[x][y] == EL_AMOEBA_DEAD ||
8718          Feld[x][y] == EL_BD_AMOEBA ||
8719          Feld[x][y] == EL_AMOEBA_GROWING))
8720     {
8721       AmoebaNr[x][y] = 0;
8722       Feld[x][y] = new_element;
8723       InitField(x, y, FALSE);
8724       TEST_DrawLevelField(x, y);
8725       done = TRUE;
8726     }
8727   }
8728
8729   if (done)
8730     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8731                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8732                             SND_BD_AMOEBA_TURNING_TO_GEM));
8733 }
8734
8735 static void AmoebeWaechst(int x, int y)
8736 {
8737   static unsigned int sound_delay = 0;
8738   static unsigned int sound_delay_value = 0;
8739
8740   if (!MovDelay[x][y])          // start new growing cycle
8741   {
8742     MovDelay[x][y] = 7;
8743
8744     if (DelayReached(&sound_delay, sound_delay_value))
8745     {
8746       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8747       sound_delay_value = 30;
8748     }
8749   }
8750
8751   if (MovDelay[x][y])           // wait some time before growing bigger
8752   {
8753     MovDelay[x][y]--;
8754     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8755     {
8756       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8757                                            6 - MovDelay[x][y]);
8758
8759       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8760     }
8761
8762     if (!MovDelay[x][y])
8763     {
8764       Feld[x][y] = Store[x][y];
8765       Store[x][y] = 0;
8766       TEST_DrawLevelField(x, y);
8767     }
8768   }
8769 }
8770
8771 static void AmoebaDisappearing(int x, int y)
8772 {
8773   static unsigned int sound_delay = 0;
8774   static unsigned int sound_delay_value = 0;
8775
8776   if (!MovDelay[x][y])          // start new shrinking cycle
8777   {
8778     MovDelay[x][y] = 7;
8779
8780     if (DelayReached(&sound_delay, sound_delay_value))
8781       sound_delay_value = 30;
8782   }
8783
8784   if (MovDelay[x][y])           // wait some time before shrinking
8785   {
8786     MovDelay[x][y]--;
8787     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8788     {
8789       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8790                                            6 - MovDelay[x][y]);
8791
8792       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8793     }
8794
8795     if (!MovDelay[x][y])
8796     {
8797       Feld[x][y] = EL_EMPTY;
8798       TEST_DrawLevelField(x, y);
8799
8800       // don't let mole enter this field in this cycle;
8801       // (give priority to objects falling to this field from above)
8802       Stop[x][y] = TRUE;
8803     }
8804   }
8805 }
8806
8807 static void AmoebeAbleger(int ax, int ay)
8808 {
8809   int i;
8810   int element = Feld[ax][ay];
8811   int graphic = el2img(element);
8812   int newax = ax, neway = ay;
8813   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8814   static int xy[4][2] =
8815   {
8816     { 0, -1 },
8817     { -1, 0 },
8818     { +1, 0 },
8819     { 0, +1 }
8820   };
8821
8822   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8823   {
8824     Feld[ax][ay] = EL_AMOEBA_DEAD;
8825     TEST_DrawLevelField(ax, ay);
8826     return;
8827   }
8828
8829   if (IS_ANIMATED(graphic))
8830     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8831
8832   if (!MovDelay[ax][ay])        // start making new amoeba field
8833     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8834
8835   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8836   {
8837     MovDelay[ax][ay]--;
8838     if (MovDelay[ax][ay])
8839       return;
8840   }
8841
8842   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8843   {
8844     int start = RND(4);
8845     int x = ax + xy[start][0];
8846     int y = ay + xy[start][1];
8847
8848     if (!IN_LEV_FIELD(x, y))
8849       return;
8850
8851     if (IS_FREE(x, y) ||
8852         CAN_GROW_INTO(Feld[x][y]) ||
8853         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8854         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8855     {
8856       newax = x;
8857       neway = y;
8858     }
8859
8860     if (newax == ax && neway == ay)
8861       return;
8862   }
8863   else                          // normal or "filled" (BD style) amoeba
8864   {
8865     int start = RND(4);
8866     boolean waiting_for_player = FALSE;
8867
8868     for (i = 0; i < NUM_DIRECTIONS; i++)
8869     {
8870       int j = (start + i) % 4;
8871       int x = ax + xy[j][0];
8872       int y = ay + xy[j][1];
8873
8874       if (!IN_LEV_FIELD(x, y))
8875         continue;
8876
8877       if (IS_FREE(x, y) ||
8878           CAN_GROW_INTO(Feld[x][y]) ||
8879           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8880           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8881       {
8882         newax = x;
8883         neway = y;
8884         break;
8885       }
8886       else if (IS_PLAYER(x, y))
8887         waiting_for_player = TRUE;
8888     }
8889
8890     if (newax == ax && neway == ay)             // amoeba cannot grow
8891     {
8892       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8893       {
8894         Feld[ax][ay] = EL_AMOEBA_DEAD;
8895         TEST_DrawLevelField(ax, ay);
8896         AmoebaCnt[AmoebaNr[ax][ay]]--;
8897
8898         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8899         {
8900           if (element == EL_AMOEBA_FULL)
8901             AmoebeUmwandeln(ax, ay);
8902           else if (element == EL_BD_AMOEBA)
8903             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8904         }
8905       }
8906       return;
8907     }
8908     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8909     {
8910       // amoeba gets larger by growing in some direction
8911
8912       int new_group_nr = AmoebaNr[ax][ay];
8913
8914 #ifdef DEBUG
8915   if (new_group_nr == 0)
8916   {
8917     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8918     printf("AmoebeAbleger(): This should never happen!\n");
8919     return;
8920   }
8921 #endif
8922
8923       AmoebaNr[newax][neway] = new_group_nr;
8924       AmoebaCnt[new_group_nr]++;
8925       AmoebaCnt2[new_group_nr]++;
8926
8927       // if amoeba touches other amoeba(s) after growing, unify them
8928       AmoebenVereinigen(newax, neway);
8929
8930       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8931       {
8932         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8933         return;
8934       }
8935     }
8936   }
8937
8938   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8939       (neway == lev_fieldy - 1 && newax != ax))
8940   {
8941     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8942     Store[newax][neway] = element;
8943   }
8944   else if (neway == ay || element == EL_EMC_DRIPPER)
8945   {
8946     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8947
8948     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8949   }
8950   else
8951   {
8952     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8953     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8954     Store[ax][ay] = EL_AMOEBA_DROP;
8955     ContinueMoving(ax, ay);
8956     return;
8957   }
8958
8959   TEST_DrawLevelField(newax, neway);
8960 }
8961
8962 static void Life(int ax, int ay)
8963 {
8964   int x1, y1, x2, y2;
8965   int life_time = 40;
8966   int element = Feld[ax][ay];
8967   int graphic = el2img(element);
8968   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8969                          level.biomaze);
8970   boolean changed = FALSE;
8971
8972   if (IS_ANIMATED(graphic))
8973     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8974
8975   if (Stop[ax][ay])
8976     return;
8977
8978   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8979     MovDelay[ax][ay] = life_time;
8980
8981   if (MovDelay[ax][ay])         // wait some time before next cycle
8982   {
8983     MovDelay[ax][ay]--;
8984     if (MovDelay[ax][ay])
8985       return;
8986   }
8987
8988   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8989   {
8990     int xx = ax+x1, yy = ay+y1;
8991     int old_element = Feld[xx][yy];
8992     int num_neighbours = 0;
8993
8994     if (!IN_LEV_FIELD(xx, yy))
8995       continue;
8996
8997     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8998     {
8999       int x = xx+x2, y = yy+y2;
9000
9001       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9002         continue;
9003
9004       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9005       boolean is_neighbour = FALSE;
9006
9007       if (level.use_life_bugs)
9008         is_neighbour =
9009           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9010            (IS_FREE(x, y)                             &&  Stop[x][y]));
9011       else
9012         is_neighbour =
9013           (Last[x][y] == element || is_player_cell);
9014
9015       if (is_neighbour)
9016         num_neighbours++;
9017     }
9018
9019     boolean is_free = FALSE;
9020
9021     if (level.use_life_bugs)
9022       is_free = (IS_FREE(xx, yy));
9023     else
9024       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9025
9026     if (xx == ax && yy == ay)           // field in the middle
9027     {
9028       if (num_neighbours < life_parameter[0] ||
9029           num_neighbours > life_parameter[1])
9030       {
9031         Feld[xx][yy] = EL_EMPTY;
9032         if (Feld[xx][yy] != old_element)
9033           TEST_DrawLevelField(xx, yy);
9034         Stop[xx][yy] = TRUE;
9035         changed = TRUE;
9036       }
9037     }
9038     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9039     {                                   // free border field
9040       if (num_neighbours >= life_parameter[2] &&
9041           num_neighbours <= life_parameter[3])
9042       {
9043         Feld[xx][yy] = element;
9044         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9045         if (Feld[xx][yy] != old_element)
9046           TEST_DrawLevelField(xx, yy);
9047         Stop[xx][yy] = TRUE;
9048         changed = TRUE;
9049       }
9050     }
9051   }
9052
9053   if (changed)
9054     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9055                    SND_GAME_OF_LIFE_GROWING);
9056 }
9057
9058 static void InitRobotWheel(int x, int y)
9059 {
9060   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9061 }
9062
9063 static void RunRobotWheel(int x, int y)
9064 {
9065   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9066 }
9067
9068 static void StopRobotWheel(int x, int y)
9069 {
9070   if (game.robot_wheel_x == x &&
9071       game.robot_wheel_y == y)
9072   {
9073     game.robot_wheel_x = -1;
9074     game.robot_wheel_y = -1;
9075     game.robot_wheel_active = FALSE;
9076   }
9077 }
9078
9079 static void InitTimegateWheel(int x, int y)
9080 {
9081   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9082 }
9083
9084 static void RunTimegateWheel(int x, int y)
9085 {
9086   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9087 }
9088
9089 static void InitMagicBallDelay(int x, int y)
9090 {
9091   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9092 }
9093
9094 static void ActivateMagicBall(int bx, int by)
9095 {
9096   int x, y;
9097
9098   if (level.ball_random)
9099   {
9100     int pos_border = RND(8);    // select one of the eight border elements
9101     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9102     int xx = pos_content % 3;
9103     int yy = pos_content / 3;
9104
9105     x = bx - 1 + xx;
9106     y = by - 1 + yy;
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   else
9112   {
9113     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9114     {
9115       int xx = x - bx + 1;
9116       int yy = y - by + 1;
9117
9118       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9119         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9120     }
9121   }
9122
9123   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9124 }
9125
9126 static void CheckExit(int x, int y)
9127 {
9128   if (game.gems_still_needed > 0 ||
9129       game.sokoban_fields_still_needed > 0 ||
9130       game.sokoban_objects_still_needed > 0 ||
9131       game.lights_still_needed > 0)
9132   {
9133     int element = Feld[x][y];
9134     int graphic = el2img(element);
9135
9136     if (IS_ANIMATED(graphic))
9137       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9138
9139     return;
9140   }
9141
9142   // do not re-open exit door closed after last player
9143   if (game.all_players_gone)
9144     return;
9145
9146   Feld[x][y] = EL_EXIT_OPENING;
9147
9148   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9149 }
9150
9151 static void CheckExitEM(int x, int y)
9152 {
9153   if (game.gems_still_needed > 0 ||
9154       game.sokoban_fields_still_needed > 0 ||
9155       game.sokoban_objects_still_needed > 0 ||
9156       game.lights_still_needed > 0)
9157   {
9158     int element = Feld[x][y];
9159     int graphic = el2img(element);
9160
9161     if (IS_ANIMATED(graphic))
9162       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9163
9164     return;
9165   }
9166
9167   // do not re-open exit door closed after last player
9168   if (game.all_players_gone)
9169     return;
9170
9171   Feld[x][y] = EL_EM_EXIT_OPENING;
9172
9173   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9174 }
9175
9176 static void CheckExitSteel(int x, int y)
9177 {
9178   if (game.gems_still_needed > 0 ||
9179       game.sokoban_fields_still_needed > 0 ||
9180       game.sokoban_objects_still_needed > 0 ||
9181       game.lights_still_needed > 0)
9182   {
9183     int element = Feld[x][y];
9184     int graphic = el2img(element);
9185
9186     if (IS_ANIMATED(graphic))
9187       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9188
9189     return;
9190   }
9191
9192   // do not re-open exit door closed after last player
9193   if (game.all_players_gone)
9194     return;
9195
9196   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9197
9198   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9199 }
9200
9201 static void CheckExitSteelEM(int x, int y)
9202 {
9203   if (game.gems_still_needed > 0 ||
9204       game.sokoban_fields_still_needed > 0 ||
9205       game.sokoban_objects_still_needed > 0 ||
9206       game.lights_still_needed > 0)
9207   {
9208     int element = Feld[x][y];
9209     int graphic = el2img(element);
9210
9211     if (IS_ANIMATED(graphic))
9212       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9213
9214     return;
9215   }
9216
9217   // do not re-open exit door closed after last player
9218   if (game.all_players_gone)
9219     return;
9220
9221   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9222
9223   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9224 }
9225
9226 static void CheckExitSP(int x, int y)
9227 {
9228   if (game.gems_still_needed > 0)
9229   {
9230     int element = Feld[x][y];
9231     int graphic = el2img(element);
9232
9233     if (IS_ANIMATED(graphic))
9234       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9235
9236     return;
9237   }
9238
9239   // do not re-open exit door closed after last player
9240   if (game.all_players_gone)
9241     return;
9242
9243   Feld[x][y] = EL_SP_EXIT_OPENING;
9244
9245   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9246 }
9247
9248 static void CloseAllOpenTimegates(void)
9249 {
9250   int x, y;
9251
9252   SCAN_PLAYFIELD(x, y)
9253   {
9254     int element = Feld[x][y];
9255
9256     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9257     {
9258       Feld[x][y] = EL_TIMEGATE_CLOSING;
9259
9260       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9261     }
9262   }
9263 }
9264
9265 static void DrawTwinkleOnField(int x, int y)
9266 {
9267   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9268     return;
9269
9270   if (Feld[x][y] == EL_BD_DIAMOND)
9271     return;
9272
9273   if (MovDelay[x][y] == 0)      // next animation frame
9274     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9275
9276   if (MovDelay[x][y] != 0)      // wait some time before next frame
9277   {
9278     MovDelay[x][y]--;
9279
9280     DrawLevelElementAnimation(x, y, Feld[x][y]);
9281
9282     if (MovDelay[x][y] != 0)
9283     {
9284       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9285                                            10 - MovDelay[x][y]);
9286
9287       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9288     }
9289   }
9290 }
9291
9292 static void MauerWaechst(int x, int y)
9293 {
9294   int delay = 6;
9295
9296   if (!MovDelay[x][y])          // next animation frame
9297     MovDelay[x][y] = 3 * delay;
9298
9299   if (MovDelay[x][y])           // wait some time before next frame
9300   {
9301     MovDelay[x][y]--;
9302
9303     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9304     {
9305       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9306       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9307
9308       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9309     }
9310
9311     if (!MovDelay[x][y])
9312     {
9313       if (MovDir[x][y] == MV_LEFT)
9314       {
9315         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9316           TEST_DrawLevelField(x - 1, y);
9317       }
9318       else if (MovDir[x][y] == MV_RIGHT)
9319       {
9320         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9321           TEST_DrawLevelField(x + 1, y);
9322       }
9323       else if (MovDir[x][y] == MV_UP)
9324       {
9325         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9326           TEST_DrawLevelField(x, y - 1);
9327       }
9328       else
9329       {
9330         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9331           TEST_DrawLevelField(x, y + 1);
9332       }
9333
9334       Feld[x][y] = Store[x][y];
9335       Store[x][y] = 0;
9336       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9337       TEST_DrawLevelField(x, y);
9338     }
9339   }
9340 }
9341
9342 static void MauerAbleger(int ax, int ay)
9343 {
9344   int element = Feld[ax][ay];
9345   int graphic = el2img(element);
9346   boolean oben_frei = FALSE, unten_frei = FALSE;
9347   boolean links_frei = FALSE, rechts_frei = FALSE;
9348   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9349   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9350   boolean new_wall = FALSE;
9351
9352   if (IS_ANIMATED(graphic))
9353     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9354
9355   if (!MovDelay[ax][ay])        // start building new wall
9356     MovDelay[ax][ay] = 6;
9357
9358   if (MovDelay[ax][ay])         // wait some time before building new wall
9359   {
9360     MovDelay[ax][ay]--;
9361     if (MovDelay[ax][ay])
9362       return;
9363   }
9364
9365   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9366     oben_frei = TRUE;
9367   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9368     unten_frei = TRUE;
9369   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9370     links_frei = TRUE;
9371   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9372     rechts_frei = TRUE;
9373
9374   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9375       element == EL_EXPANDABLE_WALL_ANY)
9376   {
9377     if (oben_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_UP;
9382       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9383         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9384                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9385       new_wall = TRUE;
9386     }
9387     if (unten_frei)
9388     {
9389       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9390       Store[ax][ay+1] = element;
9391       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9392       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9393         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9394                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9395       new_wall = TRUE;
9396     }
9397   }
9398
9399   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9400       element == EL_EXPANDABLE_WALL_ANY ||
9401       element == EL_EXPANDABLE_WALL ||
9402       element == EL_BD_EXPANDABLE_WALL)
9403   {
9404     if (links_frei)
9405     {
9406       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9407       Store[ax-1][ay] = element;
9408       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9409       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9410         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9411                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9412       new_wall = TRUE;
9413     }
9414
9415     if (rechts_frei)
9416     {
9417       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9418       Store[ax+1][ay] = element;
9419       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9420       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9421         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9422                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9423       new_wall = TRUE;
9424     }
9425   }
9426
9427   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9428     TEST_DrawLevelField(ax, ay);
9429
9430   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9431     oben_massiv = TRUE;
9432   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9433     unten_massiv = TRUE;
9434   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9435     links_massiv = TRUE;
9436   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9437     rechts_massiv = TRUE;
9438
9439   if (((oben_massiv && unten_massiv) ||
9440        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9441        element == EL_EXPANDABLE_WALL) &&
9442       ((links_massiv && rechts_massiv) ||
9443        element == EL_EXPANDABLE_WALL_VERTICAL))
9444     Feld[ax][ay] = EL_WALL;
9445
9446   if (new_wall)
9447     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9448 }
9449
9450 static void MauerAblegerStahl(int ax, int ay)
9451 {
9452   int element = Feld[ax][ay];
9453   int graphic = el2img(element);
9454   boolean oben_frei = FALSE, unten_frei = FALSE;
9455   boolean links_frei = FALSE, rechts_frei = FALSE;
9456   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9457   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9458   boolean new_wall = FALSE;
9459
9460   if (IS_ANIMATED(graphic))
9461     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9462
9463   if (!MovDelay[ax][ay])        // start building new wall
9464     MovDelay[ax][ay] = 6;
9465
9466   if (MovDelay[ax][ay])         // wait some time before building new wall
9467   {
9468     MovDelay[ax][ay]--;
9469     if (MovDelay[ax][ay])
9470       return;
9471   }
9472
9473   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9474     oben_frei = TRUE;
9475   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9476     unten_frei = TRUE;
9477   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9478     links_frei = TRUE;
9479   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9480     rechts_frei = TRUE;
9481
9482   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9483       element == EL_EXPANDABLE_STEELWALL_ANY)
9484   {
9485     if (oben_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_UP;
9490       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9491         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9492                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9493       new_wall = TRUE;
9494     }
9495     if (unten_frei)
9496     {
9497       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9498       Store[ax][ay+1] = element;
9499       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9500       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9501         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9502                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9503       new_wall = TRUE;
9504     }
9505   }
9506
9507   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9508       element == EL_EXPANDABLE_STEELWALL_ANY)
9509   {
9510     if (links_frei)
9511     {
9512       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9513       Store[ax-1][ay] = element;
9514       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9515       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9516         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9517                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9518       new_wall = TRUE;
9519     }
9520
9521     if (rechts_frei)
9522     {
9523       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9524       Store[ax+1][ay] = element;
9525       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9526       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9527         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9528                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9529       new_wall = TRUE;
9530     }
9531   }
9532
9533   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9534     oben_massiv = TRUE;
9535   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9536     unten_massiv = TRUE;
9537   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9538     links_massiv = TRUE;
9539   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9540     rechts_massiv = TRUE;
9541
9542   if (((oben_massiv && unten_massiv) ||
9543        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9544       ((links_massiv && rechts_massiv) ||
9545        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9546     Feld[ax][ay] = EL_STEELWALL;
9547
9548   if (new_wall)
9549     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9550 }
9551
9552 static void CheckForDragon(int x, int y)
9553 {
9554   int i, j;
9555   boolean dragon_found = FALSE;
9556   static int xy[4][2] =
9557   {
9558     { 0, -1 },
9559     { -1, 0 },
9560     { +1, 0 },
9561     { 0, +1 }
9562   };
9563
9564   for (i = 0; i < NUM_DIRECTIONS; i++)
9565   {
9566     for (j = 0; j < 4; j++)
9567     {
9568       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9569
9570       if (IN_LEV_FIELD(xx, yy) &&
9571           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9572       {
9573         if (Feld[xx][yy] == EL_DRAGON)
9574           dragon_found = TRUE;
9575       }
9576       else
9577         break;
9578     }
9579   }
9580
9581   if (!dragon_found)
9582   {
9583     for (i = 0; i < NUM_DIRECTIONS; i++)
9584     {
9585       for (j = 0; j < 3; j++)
9586       {
9587         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9588   
9589         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9590         {
9591           Feld[xx][yy] = EL_EMPTY;
9592           TEST_DrawLevelField(xx, yy);
9593         }
9594         else
9595           break;
9596       }
9597     }
9598   }
9599 }
9600
9601 static void InitBuggyBase(int x, int y)
9602 {
9603   int element = Feld[x][y];
9604   int activating_delay = FRAMES_PER_SECOND / 4;
9605
9606   ChangeDelay[x][y] =
9607     (element == EL_SP_BUGGY_BASE ?
9608      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9609      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9610      activating_delay :
9611      element == EL_SP_BUGGY_BASE_ACTIVE ?
9612      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9613 }
9614
9615 static void WarnBuggyBase(int x, int y)
9616 {
9617   int i;
9618   static int xy[4][2] =
9619   {
9620     { 0, -1 },
9621     { -1, 0 },
9622     { +1, 0 },
9623     { 0, +1 }
9624   };
9625
9626   for (i = 0; i < NUM_DIRECTIONS; i++)
9627   {
9628     int xx = x + xy[i][0];
9629     int yy = y + xy[i][1];
9630
9631     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9632     {
9633       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9634
9635       break;
9636     }
9637   }
9638 }
9639
9640 static void InitTrap(int x, int y)
9641 {
9642   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9643 }
9644
9645 static void ActivateTrap(int x, int y)
9646 {
9647   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9648 }
9649
9650 static void ChangeActiveTrap(int x, int y)
9651 {
9652   int graphic = IMG_TRAP_ACTIVE;
9653
9654   // if new animation frame was drawn, correct crumbled sand border
9655   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9656     TEST_DrawLevelFieldCrumbled(x, y);
9657 }
9658
9659 static int getSpecialActionElement(int element, int number, int base_element)
9660 {
9661   return (element != EL_EMPTY ? element :
9662           number != -1 ? base_element + number - 1 :
9663           EL_EMPTY);
9664 }
9665
9666 static int getModifiedActionNumber(int value_old, int operator, int operand,
9667                                    int value_min, int value_max)
9668 {
9669   int value_new = (operator == CA_MODE_SET      ? operand :
9670                    operator == CA_MODE_ADD      ? value_old + operand :
9671                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9672                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9673                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9674                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9675                    value_old);
9676
9677   return (value_new < value_min ? value_min :
9678           value_new > value_max ? value_max :
9679           value_new);
9680 }
9681
9682 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9683 {
9684   struct ElementInfo *ei = &element_info[element];
9685   struct ElementChangeInfo *change = &ei->change_page[page];
9686   int target_element = change->target_element;
9687   int action_type = change->action_type;
9688   int action_mode = change->action_mode;
9689   int action_arg = change->action_arg;
9690   int action_element = change->action_element;
9691   int i;
9692
9693   if (!change->has_action)
9694     return;
9695
9696   // ---------- determine action paramater values -----------------------------
9697
9698   int level_time_value =
9699     (level.time > 0 ? TimeLeft :
9700      TimePlayed);
9701
9702   int action_arg_element_raw =
9703     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9704      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9705      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9706      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9707      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9708      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9709      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9710      EL_EMPTY);
9711   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9712
9713   int action_arg_direction =
9714     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9715      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9716      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9717      change->actual_trigger_side :
9718      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9719      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9720      MV_NONE);
9721
9722   int action_arg_number_min =
9723     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9724      CA_ARG_MIN);
9725
9726   int action_arg_number_max =
9727     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9728      action_type == CA_SET_LEVEL_GEMS ? 999 :
9729      action_type == CA_SET_LEVEL_TIME ? 9999 :
9730      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9731      action_type == CA_SET_CE_VALUE ? 9999 :
9732      action_type == CA_SET_CE_SCORE ? 9999 :
9733      CA_ARG_MAX);
9734
9735   int action_arg_number_reset =
9736     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9737      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9738      action_type == CA_SET_LEVEL_TIME ? level.time :
9739      action_type == CA_SET_LEVEL_SCORE ? 0 :
9740      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9741      action_type == CA_SET_CE_SCORE ? 0 :
9742      0);
9743
9744   int action_arg_number =
9745     (action_arg <= CA_ARG_MAX ? action_arg :
9746      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9747      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9748      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9749      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9750      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9751      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9752      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9753      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9754      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9755      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9756      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9757      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9758      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9759      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9760      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9761      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9762      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9763      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9764      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9765      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9766      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9767      -1);
9768
9769   int action_arg_number_old =
9770     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9771      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9772      action_type == CA_SET_LEVEL_SCORE ? game.score :
9773      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9774      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9775      0);
9776
9777   int action_arg_number_new =
9778     getModifiedActionNumber(action_arg_number_old,
9779                             action_mode, action_arg_number,
9780                             action_arg_number_min, action_arg_number_max);
9781
9782   int trigger_player_bits =
9783     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9784      change->actual_trigger_player_bits : change->trigger_player);
9785
9786   int action_arg_player_bits =
9787     (action_arg >= CA_ARG_PLAYER_1 &&
9788      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9789      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9790      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9791      PLAYER_BITS_ANY);
9792
9793   // ---------- execute action  -----------------------------------------------
9794
9795   switch (action_type)
9796   {
9797     case CA_NO_ACTION:
9798     {
9799       return;
9800     }
9801
9802     // ---------- level actions  ----------------------------------------------
9803
9804     case CA_RESTART_LEVEL:
9805     {
9806       game.restart_level = TRUE;
9807
9808       break;
9809     }
9810
9811     case CA_SHOW_ENVELOPE:
9812     {
9813       int element = getSpecialActionElement(action_arg_element,
9814                                             action_arg_number, EL_ENVELOPE_1);
9815
9816       if (IS_ENVELOPE(element))
9817         local_player->show_envelope = element;
9818
9819       break;
9820     }
9821
9822     case CA_SET_LEVEL_TIME:
9823     {
9824       if (level.time > 0)       // only modify limited time value
9825       {
9826         TimeLeft = action_arg_number_new;
9827
9828         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9829
9830         DisplayGameControlValues();
9831
9832         if (!TimeLeft && setup.time_limit)
9833           for (i = 0; i < MAX_PLAYERS; i++)
9834             KillPlayer(&stored_player[i]);
9835       }
9836
9837       break;
9838     }
9839
9840     case CA_SET_LEVEL_SCORE:
9841     {
9842       game.score = action_arg_number_new;
9843
9844       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9845
9846       DisplayGameControlValues();
9847
9848       break;
9849     }
9850
9851     case CA_SET_LEVEL_GEMS:
9852     {
9853       game.gems_still_needed = action_arg_number_new;
9854
9855       game.snapshot.collected_item = TRUE;
9856
9857       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9858
9859       DisplayGameControlValues();
9860
9861       break;
9862     }
9863
9864     case CA_SET_LEVEL_WIND:
9865     {
9866       game.wind_direction = action_arg_direction;
9867
9868       break;
9869     }
9870
9871     case CA_SET_LEVEL_RANDOM_SEED:
9872     {
9873       // ensure that setting a new random seed while playing is predictable
9874       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9875
9876       break;
9877     }
9878
9879     // ---------- player actions  ---------------------------------------------
9880
9881     case CA_MOVE_PLAYER:
9882     {
9883       // automatically move to the next field in specified direction
9884       for (i = 0; i < MAX_PLAYERS; i++)
9885         if (trigger_player_bits & (1 << i))
9886           stored_player[i].programmed_action = action_arg_direction;
9887
9888       break;
9889     }
9890
9891     case CA_EXIT_PLAYER:
9892     {
9893       for (i = 0; i < MAX_PLAYERS; i++)
9894         if (action_arg_player_bits & (1 << i))
9895           ExitPlayer(&stored_player[i]);
9896
9897       if (game.players_still_needed == 0)
9898         LevelSolved();
9899
9900       break;
9901     }
9902
9903     case CA_KILL_PLAYER:
9904     {
9905       for (i = 0; i < MAX_PLAYERS; i++)
9906         if (action_arg_player_bits & (1 << i))
9907           KillPlayer(&stored_player[i]);
9908
9909       break;
9910     }
9911
9912     case CA_SET_PLAYER_KEYS:
9913     {
9914       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9915       int element = getSpecialActionElement(action_arg_element,
9916                                             action_arg_number, EL_KEY_1);
9917
9918       if (IS_KEY(element))
9919       {
9920         for (i = 0; i < MAX_PLAYERS; i++)
9921         {
9922           if (trigger_player_bits & (1 << i))
9923           {
9924             stored_player[i].key[KEY_NR(element)] = key_state;
9925
9926             DrawGameDoorValues();
9927           }
9928         }
9929       }
9930
9931       break;
9932     }
9933
9934     case CA_SET_PLAYER_SPEED:
9935     {
9936       for (i = 0; i < MAX_PLAYERS; i++)
9937       {
9938         if (trigger_player_bits & (1 << i))
9939         {
9940           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9941
9942           if (action_arg == CA_ARG_SPEED_FASTER &&
9943               stored_player[i].cannot_move)
9944           {
9945             action_arg_number = STEPSIZE_VERY_SLOW;
9946           }
9947           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9948                    action_arg == CA_ARG_SPEED_FASTER)
9949           {
9950             action_arg_number = 2;
9951             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9952                            CA_MODE_MULTIPLY);
9953           }
9954           else if (action_arg == CA_ARG_NUMBER_RESET)
9955           {
9956             action_arg_number = level.initial_player_stepsize[i];
9957           }
9958
9959           move_stepsize =
9960             getModifiedActionNumber(move_stepsize,
9961                                     action_mode,
9962                                     action_arg_number,
9963                                     action_arg_number_min,
9964                                     action_arg_number_max);
9965
9966           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9967         }
9968       }
9969
9970       break;
9971     }
9972
9973     case CA_SET_PLAYER_SHIELD:
9974     {
9975       for (i = 0; i < MAX_PLAYERS; i++)
9976       {
9977         if (trigger_player_bits & (1 << i))
9978         {
9979           if (action_arg == CA_ARG_SHIELD_OFF)
9980           {
9981             stored_player[i].shield_normal_time_left = 0;
9982             stored_player[i].shield_deadly_time_left = 0;
9983           }
9984           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9985           {
9986             stored_player[i].shield_normal_time_left = 999999;
9987           }
9988           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9989           {
9990             stored_player[i].shield_normal_time_left = 999999;
9991             stored_player[i].shield_deadly_time_left = 999999;
9992           }
9993         }
9994       }
9995
9996       break;
9997     }
9998
9999     case CA_SET_PLAYER_GRAVITY:
10000     {
10001       for (i = 0; i < MAX_PLAYERS; i++)
10002       {
10003         if (trigger_player_bits & (1 << i))
10004         {
10005           stored_player[i].gravity =
10006             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10007              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10008              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10009              stored_player[i].gravity);
10010         }
10011       }
10012
10013       break;
10014     }
10015
10016     case CA_SET_PLAYER_ARTWORK:
10017     {
10018       for (i = 0; i < MAX_PLAYERS; i++)
10019       {
10020         if (trigger_player_bits & (1 << i))
10021         {
10022           int artwork_element = action_arg_element;
10023
10024           if (action_arg == CA_ARG_ELEMENT_RESET)
10025             artwork_element =
10026               (level.use_artwork_element[i] ? level.artwork_element[i] :
10027                stored_player[i].element_nr);
10028
10029           if (stored_player[i].artwork_element != artwork_element)
10030             stored_player[i].Frame = 0;
10031
10032           stored_player[i].artwork_element = artwork_element;
10033
10034           SetPlayerWaiting(&stored_player[i], FALSE);
10035
10036           // set number of special actions for bored and sleeping animation
10037           stored_player[i].num_special_action_bored =
10038             get_num_special_action(artwork_element,
10039                                    ACTION_BORING_1, ACTION_BORING_LAST);
10040           stored_player[i].num_special_action_sleeping =
10041             get_num_special_action(artwork_element,
10042                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10043         }
10044       }
10045
10046       break;
10047     }
10048
10049     case CA_SET_PLAYER_INVENTORY:
10050     {
10051       for (i = 0; i < MAX_PLAYERS; i++)
10052       {
10053         struct PlayerInfo *player = &stored_player[i];
10054         int j, k;
10055
10056         if (trigger_player_bits & (1 << i))
10057         {
10058           int inventory_element = action_arg_element;
10059
10060           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10061               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10062               action_arg == CA_ARG_ELEMENT_ACTION)
10063           {
10064             int element = inventory_element;
10065             int collect_count = element_info[element].collect_count_initial;
10066
10067             if (!IS_CUSTOM_ELEMENT(element))
10068               collect_count = 1;
10069
10070             if (collect_count == 0)
10071               player->inventory_infinite_element = element;
10072             else
10073               for (k = 0; k < collect_count; k++)
10074                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10075                   player->inventory_element[player->inventory_size++] =
10076                     element;
10077           }
10078           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10079                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10080                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10081           {
10082             if (player->inventory_infinite_element != EL_UNDEFINED &&
10083                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10084                                      action_arg_element_raw))
10085               player->inventory_infinite_element = EL_UNDEFINED;
10086
10087             for (k = 0, j = 0; j < player->inventory_size; j++)
10088             {
10089               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10090                                         action_arg_element_raw))
10091                 player->inventory_element[k++] = player->inventory_element[j];
10092             }
10093
10094             player->inventory_size = k;
10095           }
10096           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10097           {
10098             if (player->inventory_size > 0)
10099             {
10100               for (j = 0; j < player->inventory_size - 1; j++)
10101                 player->inventory_element[j] = player->inventory_element[j + 1];
10102
10103               player->inventory_size--;
10104             }
10105           }
10106           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10107           {
10108             if (player->inventory_size > 0)
10109               player->inventory_size--;
10110           }
10111           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10112           {
10113             player->inventory_infinite_element = EL_UNDEFINED;
10114             player->inventory_size = 0;
10115           }
10116           else if (action_arg == CA_ARG_INVENTORY_RESET)
10117           {
10118             player->inventory_infinite_element = EL_UNDEFINED;
10119             player->inventory_size = 0;
10120
10121             if (level.use_initial_inventory[i])
10122             {
10123               for (j = 0; j < level.initial_inventory_size[i]; j++)
10124               {
10125                 int element = level.initial_inventory_content[i][j];
10126                 int collect_count = element_info[element].collect_count_initial;
10127
10128                 if (!IS_CUSTOM_ELEMENT(element))
10129                   collect_count = 1;
10130
10131                 if (collect_count == 0)
10132                   player->inventory_infinite_element = element;
10133                 else
10134                   for (k = 0; k < collect_count; k++)
10135                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10136                       player->inventory_element[player->inventory_size++] =
10137                         element;
10138               }
10139             }
10140           }
10141         }
10142       }
10143
10144       break;
10145     }
10146
10147     // ---------- CE actions  -------------------------------------------------
10148
10149     case CA_SET_CE_VALUE:
10150     {
10151       int last_ce_value = CustomValue[x][y];
10152
10153       CustomValue[x][y] = action_arg_number_new;
10154
10155       if (CustomValue[x][y] != last_ce_value)
10156       {
10157         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10158         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10159
10160         if (CustomValue[x][y] == 0)
10161         {
10162           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10163           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10164         }
10165       }
10166
10167       break;
10168     }
10169
10170     case CA_SET_CE_SCORE:
10171     {
10172       int last_ce_score = ei->collect_score;
10173
10174       ei->collect_score = action_arg_number_new;
10175
10176       if (ei->collect_score != last_ce_score)
10177       {
10178         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10179         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10180
10181         if (ei->collect_score == 0)
10182         {
10183           int xx, yy;
10184
10185           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10186           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10187
10188           /*
10189             This is a very special case that seems to be a mixture between
10190             CheckElementChange() and CheckTriggeredElementChange(): while
10191             the first one only affects single elements that are triggered
10192             directly, the second one affects multiple elements in the playfield
10193             that are triggered indirectly by another element. This is a third
10194             case: Changing the CE score always affects multiple identical CEs,
10195             so every affected CE must be checked, not only the single CE for
10196             which the CE score was changed in the first place (as every instance
10197             of that CE shares the same CE score, and therefore also can change)!
10198           */
10199           SCAN_PLAYFIELD(xx, yy)
10200           {
10201             if (Feld[xx][yy] == element)
10202               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10203                                  CE_SCORE_GETS_ZERO);
10204           }
10205         }
10206       }
10207
10208       break;
10209     }
10210
10211     case CA_SET_CE_ARTWORK:
10212     {
10213       int artwork_element = action_arg_element;
10214       boolean reset_frame = FALSE;
10215       int xx, yy;
10216
10217       if (action_arg == CA_ARG_ELEMENT_RESET)
10218         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10219                            element);
10220
10221       if (ei->gfx_element != artwork_element)
10222         reset_frame = TRUE;
10223
10224       ei->gfx_element = artwork_element;
10225
10226       SCAN_PLAYFIELD(xx, yy)
10227       {
10228         if (Feld[xx][yy] == element)
10229         {
10230           if (reset_frame)
10231           {
10232             ResetGfxAnimation(xx, yy);
10233             ResetRandomAnimationValue(xx, yy);
10234           }
10235
10236           TEST_DrawLevelField(xx, yy);
10237         }
10238       }
10239
10240       break;
10241     }
10242
10243     // ---------- engine actions  ---------------------------------------------
10244
10245     case CA_SET_ENGINE_SCAN_MODE:
10246     {
10247       InitPlayfieldScanMode(action_arg);
10248
10249       break;
10250     }
10251
10252     default:
10253       break;
10254   }
10255 }
10256
10257 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10258 {
10259   int old_element = Feld[x][y];
10260   int new_element = GetElementFromGroupElement(element);
10261   int previous_move_direction = MovDir[x][y];
10262   int last_ce_value = CustomValue[x][y];
10263   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10264   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10265   boolean add_player_onto_element = (new_element_is_player &&
10266                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10267                                      IS_WALKABLE(old_element));
10268
10269   if (!add_player_onto_element)
10270   {
10271     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10272       RemoveMovingField(x, y);
10273     else
10274       RemoveField(x, y);
10275
10276     Feld[x][y] = new_element;
10277
10278     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10279       MovDir[x][y] = previous_move_direction;
10280
10281     if (element_info[new_element].use_last_ce_value)
10282       CustomValue[x][y] = last_ce_value;
10283
10284     InitField_WithBug1(x, y, FALSE);
10285
10286     new_element = Feld[x][y];   // element may have changed
10287
10288     ResetGfxAnimation(x, y);
10289     ResetRandomAnimationValue(x, y);
10290
10291     TEST_DrawLevelField(x, y);
10292
10293     if (GFX_CRUMBLED(new_element))
10294       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10295   }
10296
10297   // check if element under the player changes from accessible to unaccessible
10298   // (needed for special case of dropping element which then changes)
10299   // (must be checked after creating new element for walkable group elements)
10300   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10301       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10302   {
10303     Bang(x, y);
10304
10305     return;
10306   }
10307
10308   // "ChangeCount" not set yet to allow "entered by player" change one time
10309   if (new_element_is_player)
10310     RelocatePlayer(x, y, new_element);
10311
10312   if (is_change)
10313     ChangeCount[x][y]++;        // count number of changes in the same frame
10314
10315   TestIfBadThingTouchesPlayer(x, y);
10316   TestIfPlayerTouchesCustomElement(x, y);
10317   TestIfElementTouchesCustomElement(x, y);
10318 }
10319
10320 static void CreateField(int x, int y, int element)
10321 {
10322   CreateFieldExt(x, y, element, FALSE);
10323 }
10324
10325 static void CreateElementFromChange(int x, int y, int element)
10326 {
10327   element = GET_VALID_RUNTIME_ELEMENT(element);
10328
10329   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10330   {
10331     int old_element = Feld[x][y];
10332
10333     // prevent changed element from moving in same engine frame
10334     // unless both old and new element can either fall or move
10335     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10336         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10337       Stop[x][y] = TRUE;
10338   }
10339
10340   CreateFieldExt(x, y, element, TRUE);
10341 }
10342
10343 static boolean ChangeElement(int x, int y, int element, int page)
10344 {
10345   struct ElementInfo *ei = &element_info[element];
10346   struct ElementChangeInfo *change = &ei->change_page[page];
10347   int ce_value = CustomValue[x][y];
10348   int ce_score = ei->collect_score;
10349   int target_element;
10350   int old_element = Feld[x][y];
10351
10352   // always use default change event to prevent running into a loop
10353   if (ChangeEvent[x][y] == -1)
10354     ChangeEvent[x][y] = CE_DELAY;
10355
10356   if (ChangeEvent[x][y] == CE_DELAY)
10357   {
10358     // reset actual trigger element, trigger player and action element
10359     change->actual_trigger_element = EL_EMPTY;
10360     change->actual_trigger_player = EL_EMPTY;
10361     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10362     change->actual_trigger_side = CH_SIDE_NONE;
10363     change->actual_trigger_ce_value = 0;
10364     change->actual_trigger_ce_score = 0;
10365   }
10366
10367   // do not change elements more than a specified maximum number of changes
10368   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10369     return FALSE;
10370
10371   ChangeCount[x][y]++;          // count number of changes in the same frame
10372
10373   if (change->explode)
10374   {
10375     Bang(x, y);
10376
10377     return TRUE;
10378   }
10379
10380   if (change->use_target_content)
10381   {
10382     boolean complete_replace = TRUE;
10383     boolean can_replace[3][3];
10384     int xx, yy;
10385
10386     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10387     {
10388       boolean is_empty;
10389       boolean is_walkable;
10390       boolean is_diggable;
10391       boolean is_collectible;
10392       boolean is_removable;
10393       boolean is_destructible;
10394       int ex = x + xx - 1;
10395       int ey = y + yy - 1;
10396       int content_element = change->target_content.e[xx][yy];
10397       int e;
10398
10399       can_replace[xx][yy] = TRUE;
10400
10401       if (ex == x && ey == y)   // do not check changing element itself
10402         continue;
10403
10404       if (content_element == EL_EMPTY_SPACE)
10405       {
10406         can_replace[xx][yy] = FALSE;    // do not replace border with space
10407
10408         continue;
10409       }
10410
10411       if (!IN_LEV_FIELD(ex, ey))
10412       {
10413         can_replace[xx][yy] = FALSE;
10414         complete_replace = FALSE;
10415
10416         continue;
10417       }
10418
10419       e = Feld[ex][ey];
10420
10421       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10422         e = MovingOrBlocked2Element(ex, ey);
10423
10424       is_empty = (IS_FREE(ex, ey) ||
10425                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10426
10427       is_walkable     = (is_empty || IS_WALKABLE(e));
10428       is_diggable     = (is_empty || IS_DIGGABLE(e));
10429       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10430       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10431       is_removable    = (is_diggable || is_collectible);
10432
10433       can_replace[xx][yy] =
10434         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10435           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10436           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10437           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10438           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10439           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10440          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10441
10442       if (!can_replace[xx][yy])
10443         complete_replace = FALSE;
10444     }
10445
10446     if (!change->only_if_complete || complete_replace)
10447     {
10448       boolean something_has_changed = FALSE;
10449
10450       if (change->only_if_complete && change->use_random_replace &&
10451           RND(100) < change->random_percentage)
10452         return FALSE;
10453
10454       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10455       {
10456         int ex = x + xx - 1;
10457         int ey = y + yy - 1;
10458         int content_element;
10459
10460         if (can_replace[xx][yy] && (!change->use_random_replace ||
10461                                     RND(100) < change->random_percentage))
10462         {
10463           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10464             RemoveMovingField(ex, ey);
10465
10466           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10467
10468           content_element = change->target_content.e[xx][yy];
10469           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10470                                               ce_value, ce_score);
10471
10472           CreateElementFromChange(ex, ey, target_element);
10473
10474           something_has_changed = TRUE;
10475
10476           // for symmetry reasons, freeze newly created border elements
10477           if (ex != x || ey != y)
10478             Stop[ex][ey] = TRUE;        // no more moving in this frame
10479         }
10480       }
10481
10482       if (something_has_changed)
10483       {
10484         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10485         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10486       }
10487     }
10488   }
10489   else
10490   {
10491     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10492                                         ce_value, ce_score);
10493
10494     if (element == EL_DIAGONAL_GROWING ||
10495         element == EL_DIAGONAL_SHRINKING)
10496     {
10497       target_element = Store[x][y];
10498
10499       Store[x][y] = EL_EMPTY;
10500     }
10501
10502     CreateElementFromChange(x, y, target_element);
10503
10504     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10505     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10506   }
10507
10508   // this uses direct change before indirect change
10509   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10510
10511   return TRUE;
10512 }
10513
10514 static void HandleElementChange(int x, int y, int page)
10515 {
10516   int element = MovingOrBlocked2Element(x, y);
10517   struct ElementInfo *ei = &element_info[element];
10518   struct ElementChangeInfo *change = &ei->change_page[page];
10519   boolean handle_action_before_change = FALSE;
10520
10521 #ifdef DEBUG
10522   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10523       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10524   {
10525     printf("\n\n");
10526     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10527            x, y, element, element_info[element].token_name);
10528     printf("HandleElementChange(): This should never happen!\n");
10529     printf("\n\n");
10530   }
10531 #endif
10532
10533   // this can happen with classic bombs on walkable, changing elements
10534   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10535   {
10536     return;
10537   }
10538
10539   if (ChangeDelay[x][y] == 0)           // initialize element change
10540   {
10541     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10542
10543     if (change->can_change)
10544     {
10545       // !!! not clear why graphic animation should be reset at all here !!!
10546       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10547       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10548
10549       /*
10550         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10551
10552         When using an animation frame delay of 1 (this only happens with
10553         "sp_zonk.moving.left/right" in the classic graphics), the default
10554         (non-moving) animation shows wrong animation frames (while the
10555         moving animation, like "sp_zonk.moving.left/right", is correct,
10556         so this graphical bug never shows up with the classic graphics).
10557         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10558         be drawn instead of the correct frames 0,1,2,3. This is caused by
10559         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10560         an element change: First when the change delay ("ChangeDelay[][]")
10561         counter has reached zero after decrementing, then a second time in
10562         the next frame (after "GfxFrame[][]" was already incremented) when
10563         "ChangeDelay[][]" is reset to the initial delay value again.
10564
10565         This causes frame 0 to be drawn twice, while the last frame won't
10566         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10567
10568         As some animations may already be cleverly designed around this bug
10569         (at least the "Snake Bite" snake tail animation does this), it cannot
10570         simply be fixed here without breaking such existing animations.
10571         Unfortunately, it cannot easily be detected if a graphics set was
10572         designed "before" or "after" the bug was fixed. As a workaround,
10573         a new graphics set option "game.graphics_engine_version" was added
10574         to be able to specify the game's major release version for which the
10575         graphics set was designed, which can then be used to decide if the
10576         bugfix should be used (version 4 and above) or not (version 3 or
10577         below, or if no version was specified at all, as with old sets).
10578
10579         (The wrong/fixed animation frames can be tested with the test level set
10580         "test_gfxframe" and level "000", which contains a specially prepared
10581         custom element at level position (x/y) == (11/9) which uses the zonk
10582         animation mentioned above. Using "game.graphics_engine_version: 4"
10583         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10584         This can also be seen from the debug output for this test element.)
10585       */
10586
10587       // when a custom element is about to change (for example by change delay),
10588       // do not reset graphic animation when the custom element is moving
10589       if (game.graphics_engine_version < 4 &&
10590           !IS_MOVING(x, y))
10591       {
10592         ResetGfxAnimation(x, y);
10593         ResetRandomAnimationValue(x, y);
10594       }
10595
10596       if (change->pre_change_function)
10597         change->pre_change_function(x, y);
10598     }
10599   }
10600
10601   ChangeDelay[x][y]--;
10602
10603   if (ChangeDelay[x][y] != 0)           // continue element change
10604   {
10605     if (change->can_change)
10606     {
10607       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10608
10609       if (IS_ANIMATED(graphic))
10610         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10611
10612       if (change->change_function)
10613         change->change_function(x, y);
10614     }
10615   }
10616   else                                  // finish element change
10617   {
10618     if (ChangePage[x][y] != -1)         // remember page from delayed change
10619     {
10620       page = ChangePage[x][y];
10621       ChangePage[x][y] = -1;
10622
10623       change = &ei->change_page[page];
10624     }
10625
10626     if (IS_MOVING(x, y))                // never change a running system ;-)
10627     {
10628       ChangeDelay[x][y] = 1;            // try change after next move step
10629       ChangePage[x][y] = page;          // remember page to use for change
10630
10631       return;
10632     }
10633
10634     // special case: set new level random seed before changing element
10635     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10636       handle_action_before_change = TRUE;
10637
10638     if (change->has_action && handle_action_before_change)
10639       ExecuteCustomElementAction(x, y, element, page);
10640
10641     if (change->can_change)
10642     {
10643       if (ChangeElement(x, y, element, page))
10644       {
10645         if (change->post_change_function)
10646           change->post_change_function(x, y);
10647       }
10648     }
10649
10650     if (change->has_action && !handle_action_before_change)
10651       ExecuteCustomElementAction(x, y, element, page);
10652   }
10653 }
10654
10655 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10656                                               int trigger_element,
10657                                               int trigger_event,
10658                                               int trigger_player,
10659                                               int trigger_side,
10660                                               int trigger_page)
10661 {
10662   boolean change_done_any = FALSE;
10663   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10664   int i;
10665
10666   if (!(trigger_events[trigger_element][trigger_event]))
10667     return FALSE;
10668
10669   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10670
10671   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10672   {
10673     int element = EL_CUSTOM_START + i;
10674     boolean change_done = FALSE;
10675     int p;
10676
10677     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10678         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10679       continue;
10680
10681     for (p = 0; p < element_info[element].num_change_pages; p++)
10682     {
10683       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10684
10685       if (change->can_change_or_has_action &&
10686           change->has_event[trigger_event] &&
10687           change->trigger_side & trigger_side &&
10688           change->trigger_player & trigger_player &&
10689           change->trigger_page & trigger_page_bits &&
10690           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10691       {
10692         change->actual_trigger_element = trigger_element;
10693         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10694         change->actual_trigger_player_bits = trigger_player;
10695         change->actual_trigger_side = trigger_side;
10696         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10697         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10698
10699         if ((change->can_change && !change_done) || change->has_action)
10700         {
10701           int x, y;
10702
10703           SCAN_PLAYFIELD(x, y)
10704           {
10705             if (Feld[x][y] == element)
10706             {
10707               if (change->can_change && !change_done)
10708               {
10709                 // if element already changed in this frame, not only prevent
10710                 // another element change (checked in ChangeElement()), but
10711                 // also prevent additional element actions for this element
10712
10713                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10714                     !level.use_action_after_change_bug)
10715                   continue;
10716
10717                 ChangeDelay[x][y] = 1;
10718                 ChangeEvent[x][y] = trigger_event;
10719
10720                 HandleElementChange(x, y, p);
10721               }
10722               else if (change->has_action)
10723               {
10724                 // if element already changed in this frame, not only prevent
10725                 // another element change (checked in ChangeElement()), but
10726                 // also prevent additional element actions for this element
10727
10728                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10729                     !level.use_action_after_change_bug)
10730                   continue;
10731
10732                 ExecuteCustomElementAction(x, y, element, p);
10733                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10734               }
10735             }
10736           }
10737
10738           if (change->can_change)
10739           {
10740             change_done = TRUE;
10741             change_done_any = TRUE;
10742           }
10743         }
10744       }
10745     }
10746   }
10747
10748   RECURSION_LOOP_DETECTION_END();
10749
10750   return change_done_any;
10751 }
10752
10753 static boolean CheckElementChangeExt(int x, int y,
10754                                      int element,
10755                                      int trigger_element,
10756                                      int trigger_event,
10757                                      int trigger_player,
10758                                      int trigger_side)
10759 {
10760   boolean change_done = FALSE;
10761   int p;
10762
10763   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10764       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10765     return FALSE;
10766
10767   if (Feld[x][y] == EL_BLOCKED)
10768   {
10769     Blocked2Moving(x, y, &x, &y);
10770     element = Feld[x][y];
10771   }
10772
10773   // check if element has already changed or is about to change after moving
10774   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10775        Feld[x][y] != element) ||
10776
10777       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10778        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10779         ChangePage[x][y] != -1)))
10780     return FALSE;
10781
10782   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10783
10784   for (p = 0; p < element_info[element].num_change_pages; p++)
10785   {
10786     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10787
10788     /* check trigger element for all events where the element that is checked
10789        for changing interacts with a directly adjacent element -- this is
10790        different to element changes that affect other elements to change on the
10791        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10792     boolean check_trigger_element =
10793       (trigger_event == CE_TOUCHING_X ||
10794        trigger_event == CE_HITTING_X ||
10795        trigger_event == CE_HIT_BY_X ||
10796        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10797
10798     if (change->can_change_or_has_action &&
10799         change->has_event[trigger_event] &&
10800         change->trigger_side & trigger_side &&
10801         change->trigger_player & trigger_player &&
10802         (!check_trigger_element ||
10803          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10804     {
10805       change->actual_trigger_element = trigger_element;
10806       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10807       change->actual_trigger_player_bits = trigger_player;
10808       change->actual_trigger_side = trigger_side;
10809       change->actual_trigger_ce_value = CustomValue[x][y];
10810       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10811
10812       // special case: trigger element not at (x,y) position for some events
10813       if (check_trigger_element)
10814       {
10815         static struct
10816         {
10817           int dx, dy;
10818         } move_xy[] =
10819           {
10820             {  0,  0 },
10821             { -1,  0 },
10822             { +1,  0 },
10823             {  0,  0 },
10824             {  0, -1 },
10825             {  0,  0 }, { 0, 0 }, { 0, 0 },
10826             {  0, +1 }
10827           };
10828
10829         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10830         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10831
10832         change->actual_trigger_ce_value = CustomValue[xx][yy];
10833         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10834       }
10835
10836       if (change->can_change && !change_done)
10837       {
10838         ChangeDelay[x][y] = 1;
10839         ChangeEvent[x][y] = trigger_event;
10840
10841         HandleElementChange(x, y, p);
10842
10843         change_done = TRUE;
10844       }
10845       else if (change->has_action)
10846       {
10847         ExecuteCustomElementAction(x, y, element, p);
10848         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10849       }
10850     }
10851   }
10852
10853   RECURSION_LOOP_DETECTION_END();
10854
10855   return change_done;
10856 }
10857
10858 static void PlayPlayerSound(struct PlayerInfo *player)
10859 {
10860   int jx = player->jx, jy = player->jy;
10861   int sound_element = player->artwork_element;
10862   int last_action = player->last_action_waiting;
10863   int action = player->action_waiting;
10864
10865   if (player->is_waiting)
10866   {
10867     if (action != last_action)
10868       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10869     else
10870       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10871   }
10872   else
10873   {
10874     if (action != last_action)
10875       StopSound(element_info[sound_element].sound[last_action]);
10876
10877     if (last_action == ACTION_SLEEPING)
10878       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10879   }
10880 }
10881
10882 static void PlayAllPlayersSound(void)
10883 {
10884   int i;
10885
10886   for (i = 0; i < MAX_PLAYERS; i++)
10887     if (stored_player[i].active)
10888       PlayPlayerSound(&stored_player[i]);
10889 }
10890
10891 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10892 {
10893   boolean last_waiting = player->is_waiting;
10894   int move_dir = player->MovDir;
10895
10896   player->dir_waiting = move_dir;
10897   player->last_action_waiting = player->action_waiting;
10898
10899   if (is_waiting)
10900   {
10901     if (!last_waiting)          // not waiting -> waiting
10902     {
10903       player->is_waiting = TRUE;
10904
10905       player->frame_counter_bored =
10906         FrameCounter +
10907         game.player_boring_delay_fixed +
10908         GetSimpleRandom(game.player_boring_delay_random);
10909       player->frame_counter_sleeping =
10910         FrameCounter +
10911         game.player_sleeping_delay_fixed +
10912         GetSimpleRandom(game.player_sleeping_delay_random);
10913
10914       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10915     }
10916
10917     if (game.player_sleeping_delay_fixed +
10918         game.player_sleeping_delay_random > 0 &&
10919         player->anim_delay_counter == 0 &&
10920         player->post_delay_counter == 0 &&
10921         FrameCounter >= player->frame_counter_sleeping)
10922       player->is_sleeping = TRUE;
10923     else if (game.player_boring_delay_fixed +
10924              game.player_boring_delay_random > 0 &&
10925              FrameCounter >= player->frame_counter_bored)
10926       player->is_bored = TRUE;
10927
10928     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10929                               player->is_bored ? ACTION_BORING :
10930                               ACTION_WAITING);
10931
10932     if (player->is_sleeping && player->use_murphy)
10933     {
10934       // special case for sleeping Murphy when leaning against non-free tile
10935
10936       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10937           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10938            !IS_MOVING(player->jx - 1, player->jy)))
10939         move_dir = MV_LEFT;
10940       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10941                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10942                 !IS_MOVING(player->jx + 1, player->jy)))
10943         move_dir = MV_RIGHT;
10944       else
10945         player->is_sleeping = FALSE;
10946
10947       player->dir_waiting = move_dir;
10948     }
10949
10950     if (player->is_sleeping)
10951     {
10952       if (player->num_special_action_sleeping > 0)
10953       {
10954         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10955         {
10956           int last_special_action = player->special_action_sleeping;
10957           int num_special_action = player->num_special_action_sleeping;
10958           int special_action =
10959             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10960              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10961              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10962              last_special_action + 1 : ACTION_SLEEPING);
10963           int special_graphic =
10964             el_act_dir2img(player->artwork_element, special_action, move_dir);
10965
10966           player->anim_delay_counter =
10967             graphic_info[special_graphic].anim_delay_fixed +
10968             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10969           player->post_delay_counter =
10970             graphic_info[special_graphic].post_delay_fixed +
10971             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10972
10973           player->special_action_sleeping = special_action;
10974         }
10975
10976         if (player->anim_delay_counter > 0)
10977         {
10978           player->action_waiting = player->special_action_sleeping;
10979           player->anim_delay_counter--;
10980         }
10981         else if (player->post_delay_counter > 0)
10982         {
10983           player->post_delay_counter--;
10984         }
10985       }
10986     }
10987     else if (player->is_bored)
10988     {
10989       if (player->num_special_action_bored > 0)
10990       {
10991         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10992         {
10993           int special_action =
10994             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10995           int special_graphic =
10996             el_act_dir2img(player->artwork_element, special_action, move_dir);
10997
10998           player->anim_delay_counter =
10999             graphic_info[special_graphic].anim_delay_fixed +
11000             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11001           player->post_delay_counter =
11002             graphic_info[special_graphic].post_delay_fixed +
11003             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11004
11005           player->special_action_bored = special_action;
11006         }
11007
11008         if (player->anim_delay_counter > 0)
11009         {
11010           player->action_waiting = player->special_action_bored;
11011           player->anim_delay_counter--;
11012         }
11013         else if (player->post_delay_counter > 0)
11014         {
11015           player->post_delay_counter--;
11016         }
11017       }
11018     }
11019   }
11020   else if (last_waiting)        // waiting -> not waiting
11021   {
11022     player->is_waiting = FALSE;
11023     player->is_bored = FALSE;
11024     player->is_sleeping = FALSE;
11025
11026     player->frame_counter_bored = -1;
11027     player->frame_counter_sleeping = -1;
11028
11029     player->anim_delay_counter = 0;
11030     player->post_delay_counter = 0;
11031
11032     player->dir_waiting = player->MovDir;
11033     player->action_waiting = ACTION_DEFAULT;
11034
11035     player->special_action_bored = ACTION_DEFAULT;
11036     player->special_action_sleeping = ACTION_DEFAULT;
11037   }
11038 }
11039
11040 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11041 {
11042   if ((!player->is_moving  && player->was_moving) ||
11043       (player->MovPos == 0 && player->was_moving) ||
11044       (player->is_snapping && !player->was_snapping) ||
11045       (player->is_dropping && !player->was_dropping))
11046   {
11047     if (!CheckSaveEngineSnapshotToList())
11048       return;
11049
11050     player->was_moving = FALSE;
11051     player->was_snapping = TRUE;
11052     player->was_dropping = TRUE;
11053   }
11054   else
11055   {
11056     if (player->is_moving)
11057       player->was_moving = TRUE;
11058
11059     if (!player->is_snapping)
11060       player->was_snapping = FALSE;
11061
11062     if (!player->is_dropping)
11063       player->was_dropping = FALSE;
11064   }
11065 }
11066
11067 static void CheckSingleStepMode(struct PlayerInfo *player)
11068 {
11069   if (tape.single_step && tape.recording && !tape.pausing)
11070   {
11071     /* as it is called "single step mode", just return to pause mode when the
11072        player stopped moving after one tile (or never starts moving at all) */
11073     if (!player->is_moving &&
11074         !player->is_pushing &&
11075         !player->is_dropping_pressed)
11076       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11077   }
11078
11079   CheckSaveEngineSnapshot(player);
11080 }
11081
11082 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11083 {
11084   int left      = player_action & JOY_LEFT;
11085   int right     = player_action & JOY_RIGHT;
11086   int up        = player_action & JOY_UP;
11087   int down      = player_action & JOY_DOWN;
11088   int button1   = player_action & JOY_BUTTON_1;
11089   int button2   = player_action & JOY_BUTTON_2;
11090   int dx        = (left ? -1 : right ? 1 : 0);
11091   int dy        = (up   ? -1 : down  ? 1 : 0);
11092
11093   if (!player->active || tape.pausing)
11094     return 0;
11095
11096   if (player_action)
11097   {
11098     if (button1)
11099       SnapField(player, dx, dy);
11100     else
11101     {
11102       if (button2)
11103         DropElement(player);
11104
11105       MovePlayer(player, dx, dy);
11106     }
11107
11108     CheckSingleStepMode(player);
11109
11110     SetPlayerWaiting(player, FALSE);
11111
11112     return player_action;
11113   }
11114   else
11115   {
11116     // no actions for this player (no input at player's configured device)
11117
11118     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11119     SnapField(player, 0, 0);
11120     CheckGravityMovementWhenNotMoving(player);
11121
11122     if (player->MovPos == 0)
11123       SetPlayerWaiting(player, TRUE);
11124
11125     if (player->MovPos == 0)    // needed for tape.playing
11126       player->is_moving = FALSE;
11127
11128     player->is_dropping = FALSE;
11129     player->is_dropping_pressed = FALSE;
11130     player->drop_pressed_delay = 0;
11131
11132     CheckSingleStepMode(player);
11133
11134     return 0;
11135   }
11136 }
11137
11138 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11139                                          byte *tape_action)
11140 {
11141   if (!tape.use_mouse)
11142     return;
11143
11144   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11145   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11146   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11147 }
11148
11149 static void SetTapeActionFromMouseAction(byte *tape_action,
11150                                          struct MouseActionInfo *mouse_action)
11151 {
11152   if (!tape.use_mouse)
11153     return;
11154
11155   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11156   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11157   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11158 }
11159
11160 static void CheckLevelSolved(void)
11161 {
11162   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11163   {
11164     if (game_em.level_solved &&
11165         !game_em.game_over)                             // game won
11166     {
11167       LevelSolved();
11168
11169       game_em.game_over = TRUE;
11170
11171       game.all_players_gone = TRUE;
11172     }
11173
11174     if (game_em.game_over)                              // game lost
11175       game.all_players_gone = TRUE;
11176   }
11177   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11178   {
11179     if (game_sp.level_solved &&
11180         !game_sp.game_over)                             // game won
11181     {
11182       LevelSolved();
11183
11184       game_sp.game_over = TRUE;
11185
11186       game.all_players_gone = TRUE;
11187     }
11188
11189     if (game_sp.game_over)                              // game lost
11190       game.all_players_gone = TRUE;
11191   }
11192   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11193   {
11194     if (game_mm.level_solved &&
11195         !game_mm.game_over)                             // game won
11196     {
11197       LevelSolved();
11198
11199       game_mm.game_over = TRUE;
11200
11201       game.all_players_gone = TRUE;
11202     }
11203
11204     if (game_mm.game_over)                              // game lost
11205       game.all_players_gone = TRUE;
11206   }
11207 }
11208
11209 static void CheckLevelTime(void)
11210 {
11211   int i;
11212
11213   if (TimeFrames >= FRAMES_PER_SECOND)
11214   {
11215     TimeFrames = 0;
11216     TapeTime++;
11217
11218     for (i = 0; i < MAX_PLAYERS; i++)
11219     {
11220       struct PlayerInfo *player = &stored_player[i];
11221
11222       if (SHIELD_ON(player))
11223       {
11224         player->shield_normal_time_left--;
11225
11226         if (player->shield_deadly_time_left > 0)
11227           player->shield_deadly_time_left--;
11228       }
11229     }
11230
11231     if (!game.LevelSolved && !level.use_step_counter)
11232     {
11233       TimePlayed++;
11234
11235       if (TimeLeft > 0)
11236       {
11237         TimeLeft--;
11238
11239         if (TimeLeft <= 10 && setup.time_limit)
11240           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11241
11242         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11243            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11244
11245         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11246
11247         if (!TimeLeft && setup.time_limit)
11248         {
11249           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11250             game_em.lev->killed_out_of_time = TRUE;
11251           else
11252             for (i = 0; i < MAX_PLAYERS; i++)
11253               KillPlayer(&stored_player[i]);
11254         }
11255       }
11256       else if (game.no_time_limit && !game.all_players_gone)
11257       {
11258         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11259       }
11260
11261       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11262     }
11263
11264     if (tape.recording || tape.playing)
11265       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11266   }
11267
11268   if (tape.recording || tape.playing)
11269     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11270
11271   UpdateAndDisplayGameControlValues();
11272 }
11273
11274 void AdvanceFrameAndPlayerCounters(int player_nr)
11275 {
11276   int i;
11277
11278   // advance frame counters (global frame counter and time frame counter)
11279   FrameCounter++;
11280   TimeFrames++;
11281
11282   // advance player counters (counters for move delay, move animation etc.)
11283   for (i = 0; i < MAX_PLAYERS; i++)
11284   {
11285     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11286     int move_delay_value = stored_player[i].move_delay_value;
11287     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11288
11289     if (!advance_player_counters)       // not all players may be affected
11290       continue;
11291
11292     if (move_frames == 0)       // less than one move per game frame
11293     {
11294       int stepsize = TILEX / move_delay_value;
11295       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11296       int count = (stored_player[i].is_moving ?
11297                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11298
11299       if (count % delay == 0)
11300         move_frames = 1;
11301     }
11302
11303     stored_player[i].Frame += move_frames;
11304
11305     if (stored_player[i].MovPos != 0)
11306       stored_player[i].StepFrame += move_frames;
11307
11308     if (stored_player[i].move_delay > 0)
11309       stored_player[i].move_delay--;
11310
11311     // due to bugs in previous versions, counter must count up, not down
11312     if (stored_player[i].push_delay != -1)
11313       stored_player[i].push_delay++;
11314
11315     if (stored_player[i].drop_delay > 0)
11316       stored_player[i].drop_delay--;
11317
11318     if (stored_player[i].is_dropping_pressed)
11319       stored_player[i].drop_pressed_delay++;
11320   }
11321 }
11322
11323 void StartGameActions(boolean init_network_game, boolean record_tape,
11324                       int random_seed)
11325 {
11326   unsigned int new_random_seed = InitRND(random_seed);
11327
11328   if (record_tape)
11329     TapeStartRecording(new_random_seed);
11330
11331   if (init_network_game)
11332   {
11333     SendToServer_LevelFile();
11334     SendToServer_StartPlaying();
11335
11336     return;
11337   }
11338
11339   InitGame();
11340 }
11341
11342 static void GameActionsExt(void)
11343 {
11344 #if 0
11345   static unsigned int game_frame_delay = 0;
11346 #endif
11347   unsigned int game_frame_delay_value;
11348   byte *recorded_player_action;
11349   byte summarized_player_action = 0;
11350   byte tape_action[MAX_PLAYERS];
11351   int i;
11352
11353   // detect endless loops, caused by custom element programming
11354   if (recursion_loop_detected && recursion_loop_depth == 0)
11355   {
11356     char *message = getStringCat3("Internal Error! Element ",
11357                                   EL_NAME(recursion_loop_element),
11358                                   " caused endless loop! Quit the game?");
11359
11360     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11361           EL_NAME(recursion_loop_element));
11362
11363     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11364
11365     recursion_loop_detected = FALSE;    // if game should be continued
11366
11367     free(message);
11368
11369     return;
11370   }
11371
11372   if (game.restart_level)
11373     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11374
11375   CheckLevelSolved();
11376
11377   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11378     GameWon();
11379
11380   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11381     TapeStop();
11382
11383   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11384     return;
11385
11386   game_frame_delay_value =
11387     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11388
11389   if (tape.playing && tape.warp_forward && !tape.pausing)
11390     game_frame_delay_value = 0;
11391
11392   SetVideoFrameDelay(game_frame_delay_value);
11393
11394   // (de)activate virtual buttons depending on current game status
11395   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11396   {
11397     if (game.all_players_gone)  // if no players there to be controlled anymore
11398       SetOverlayActive(FALSE);
11399     else if (!tape.playing)     // if game continues after tape stopped playing
11400       SetOverlayActive(TRUE);
11401   }
11402
11403 #if 0
11404 #if 0
11405   // ---------- main game synchronization point ----------
11406
11407   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11408
11409   printf("::: skip == %d\n", skip);
11410
11411 #else
11412   // ---------- main game synchronization point ----------
11413
11414   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11415 #endif
11416 #endif
11417
11418   if (network_playing && !network_player_action_received)
11419   {
11420     // try to get network player actions in time
11421
11422     // last chance to get network player actions without main loop delay
11423     HandleNetworking();
11424
11425     // game was quit by network peer
11426     if (game_status != GAME_MODE_PLAYING)
11427       return;
11428
11429     // check if network player actions still missing and game still running
11430     if (!network_player_action_received && !checkGameEnded())
11431       return;           // failed to get network player actions in time
11432
11433     // do not yet reset "network_player_action_received" (for tape.pausing)
11434   }
11435
11436   if (tape.pausing)
11437     return;
11438
11439   // at this point we know that we really continue executing the game
11440
11441   network_player_action_received = FALSE;
11442
11443   // when playing tape, read previously recorded player input from tape data
11444   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11445
11446   local_player->effective_mouse_action = local_player->mouse_action;
11447
11448   if (recorded_player_action != NULL)
11449     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11450                                  recorded_player_action);
11451
11452   // TapePlayAction() may return NULL when toggling to "pause before death"
11453   if (tape.pausing)
11454     return;
11455
11456   if (tape.set_centered_player)
11457   {
11458     game.centered_player_nr_next = tape.centered_player_nr_next;
11459     game.set_centered_player = TRUE;
11460   }
11461
11462   for (i = 0; i < MAX_PLAYERS; i++)
11463   {
11464     summarized_player_action |= stored_player[i].action;
11465
11466     if (!network_playing && (game.team_mode || tape.playing))
11467       stored_player[i].effective_action = stored_player[i].action;
11468   }
11469
11470   if (network_playing && !checkGameEnded())
11471     SendToServer_MovePlayer(summarized_player_action);
11472
11473   // summarize all actions at local players mapped input device position
11474   // (this allows using different input devices in single player mode)
11475   if (!network.enabled && !game.team_mode)
11476     stored_player[map_player_action[local_player->index_nr]].effective_action =
11477       summarized_player_action;
11478
11479   // summarize all actions at centered player in local team mode
11480   if (tape.recording &&
11481       setup.team_mode && !network.enabled &&
11482       setup.input_on_focus &&
11483       game.centered_player_nr != -1)
11484   {
11485     for (i = 0; i < MAX_PLAYERS; i++)
11486       stored_player[map_player_action[i]].effective_action =
11487         (i == game.centered_player_nr ? summarized_player_action : 0);
11488   }
11489
11490   if (recorded_player_action != NULL)
11491     for (i = 0; i < MAX_PLAYERS; i++)
11492       stored_player[i].effective_action = recorded_player_action[i];
11493
11494   for (i = 0; i < MAX_PLAYERS; i++)
11495   {
11496     tape_action[i] = stored_player[i].effective_action;
11497
11498     /* (this may happen in the RND game engine if a player was not present on
11499        the playfield on level start, but appeared later from a custom element */
11500     if (setup.team_mode &&
11501         tape.recording &&
11502         tape_action[i] &&
11503         !tape.player_participates[i])
11504       tape.player_participates[i] = TRUE;
11505   }
11506
11507   SetTapeActionFromMouseAction(tape_action,
11508                                &local_player->effective_mouse_action);
11509
11510   // only record actions from input devices, but not programmed actions
11511   if (tape.recording)
11512     TapeRecordAction(tape_action);
11513
11514   // remember if game was played (especially after tape stopped playing)
11515   if (!tape.playing && summarized_player_action)
11516     game.GamePlayed = TRUE;
11517
11518 #if USE_NEW_PLAYER_ASSIGNMENTS
11519   // !!! also map player actions in single player mode !!!
11520   // if (game.team_mode)
11521   if (1)
11522   {
11523     byte mapped_action[MAX_PLAYERS];
11524
11525 #if DEBUG_PLAYER_ACTIONS
11526     printf(":::");
11527     for (i = 0; i < MAX_PLAYERS; i++)
11528       printf(" %d, ", stored_player[i].effective_action);
11529 #endif
11530
11531     for (i = 0; i < MAX_PLAYERS; i++)
11532       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11533
11534     for (i = 0; i < MAX_PLAYERS; i++)
11535       stored_player[i].effective_action = mapped_action[i];
11536
11537 #if DEBUG_PLAYER_ACTIONS
11538     printf(" =>");
11539     for (i = 0; i < MAX_PLAYERS; i++)
11540       printf(" %d, ", stored_player[i].effective_action);
11541     printf("\n");
11542 #endif
11543   }
11544 #if DEBUG_PLAYER_ACTIONS
11545   else
11546   {
11547     printf(":::");
11548     for (i = 0; i < MAX_PLAYERS; i++)
11549       printf(" %d, ", stored_player[i].effective_action);
11550     printf("\n");
11551   }
11552 #endif
11553 #endif
11554
11555   for (i = 0; i < MAX_PLAYERS; i++)
11556   {
11557     // allow engine snapshot in case of changed movement attempt
11558     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11559         (stored_player[i].effective_action & KEY_MOTION))
11560       game.snapshot.changed_action = TRUE;
11561
11562     // allow engine snapshot in case of snapping/dropping attempt
11563     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11564         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11565       game.snapshot.changed_action = TRUE;
11566
11567     game.snapshot.last_action[i] = stored_player[i].effective_action;
11568   }
11569
11570   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11571   {
11572     GameActions_EM_Main();
11573   }
11574   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11575   {
11576     GameActions_SP_Main();
11577   }
11578   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11579   {
11580     GameActions_MM_Main();
11581   }
11582   else
11583   {
11584     GameActions_RND_Main();
11585   }
11586
11587   BlitScreenToBitmap(backbuffer);
11588
11589   CheckLevelSolved();
11590   CheckLevelTime();
11591
11592   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11593
11594   if (global.show_frames_per_second)
11595   {
11596     static unsigned int fps_counter = 0;
11597     static int fps_frames = 0;
11598     unsigned int fps_delay_ms = Counter() - fps_counter;
11599
11600     fps_frames++;
11601
11602     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11603     {
11604       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11605
11606       fps_frames = 0;
11607       fps_counter = Counter();
11608
11609       // always draw FPS to screen after FPS value was updated
11610       redraw_mask |= REDRAW_FPS;
11611     }
11612
11613     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11614     if (GetDrawDeactivationMask() == REDRAW_NONE)
11615       redraw_mask |= REDRAW_FPS;
11616   }
11617 }
11618
11619 static void GameActions_CheckSaveEngineSnapshot(void)
11620 {
11621   if (!game.snapshot.save_snapshot)
11622     return;
11623
11624   // clear flag for saving snapshot _before_ saving snapshot
11625   game.snapshot.save_snapshot = FALSE;
11626
11627   SaveEngineSnapshotToList();
11628 }
11629
11630 void GameActions(void)
11631 {
11632   GameActionsExt();
11633
11634   GameActions_CheckSaveEngineSnapshot();
11635 }
11636
11637 void GameActions_EM_Main(void)
11638 {
11639   byte effective_action[MAX_PLAYERS];
11640   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11641   int i;
11642
11643   for (i = 0; i < MAX_PLAYERS; i++)
11644     effective_action[i] = stored_player[i].effective_action;
11645
11646   GameActions_EM(effective_action, warp_mode);
11647 }
11648
11649 void GameActions_SP_Main(void)
11650 {
11651   byte effective_action[MAX_PLAYERS];
11652   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11653   int i;
11654
11655   for (i = 0; i < MAX_PLAYERS; i++)
11656     effective_action[i] = stored_player[i].effective_action;
11657
11658   GameActions_SP(effective_action, warp_mode);
11659
11660   for (i = 0; i < MAX_PLAYERS; i++)
11661   {
11662     if (stored_player[i].force_dropping)
11663       stored_player[i].action |= KEY_BUTTON_DROP;
11664
11665     stored_player[i].force_dropping = FALSE;
11666   }
11667 }
11668
11669 void GameActions_MM_Main(void)
11670 {
11671   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11672
11673   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11674 }
11675
11676 void GameActions_RND_Main(void)
11677 {
11678   GameActions_RND();
11679 }
11680
11681 void GameActions_RND(void)
11682 {
11683   int magic_wall_x = 0, magic_wall_y = 0;
11684   int i, x, y, element, graphic, last_gfx_frame;
11685
11686   InitPlayfieldScanModeVars();
11687
11688   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11689   {
11690     SCAN_PLAYFIELD(x, y)
11691     {
11692       ChangeCount[x][y] = 0;
11693       ChangeEvent[x][y] = -1;
11694     }
11695   }
11696
11697   if (game.set_centered_player)
11698   {
11699     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11700
11701     // switching to "all players" only possible if all players fit to screen
11702     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11703     {
11704       game.centered_player_nr_next = game.centered_player_nr;
11705       game.set_centered_player = FALSE;
11706     }
11707
11708     // do not switch focus to non-existing (or non-active) player
11709     if (game.centered_player_nr_next >= 0 &&
11710         !stored_player[game.centered_player_nr_next].active)
11711     {
11712       game.centered_player_nr_next = game.centered_player_nr;
11713       game.set_centered_player = FALSE;
11714     }
11715   }
11716
11717   if (game.set_centered_player &&
11718       ScreenMovPos == 0)        // screen currently aligned at tile position
11719   {
11720     int sx, sy;
11721
11722     if (game.centered_player_nr_next == -1)
11723     {
11724       setScreenCenteredToAllPlayers(&sx, &sy);
11725     }
11726     else
11727     {
11728       sx = stored_player[game.centered_player_nr_next].jx;
11729       sy = stored_player[game.centered_player_nr_next].jy;
11730     }
11731
11732     game.centered_player_nr = game.centered_player_nr_next;
11733     game.set_centered_player = FALSE;
11734
11735     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11736     DrawGameDoorValues();
11737   }
11738
11739   for (i = 0; i < MAX_PLAYERS; i++)
11740   {
11741     int actual_player_action = stored_player[i].effective_action;
11742
11743 #if 1
11744     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11745        - rnd_equinox_tetrachloride 048
11746        - rnd_equinox_tetrachloride_ii 096
11747        - rnd_emanuel_schmieg 002
11748        - doctor_sloan_ww 001, 020
11749     */
11750     if (stored_player[i].MovPos == 0)
11751       CheckGravityMovement(&stored_player[i]);
11752 #endif
11753
11754     // overwrite programmed action with tape action
11755     if (stored_player[i].programmed_action)
11756       actual_player_action = stored_player[i].programmed_action;
11757
11758     PlayerActions(&stored_player[i], actual_player_action);
11759
11760     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11761   }
11762
11763   ScrollScreen(NULL, SCROLL_GO_ON);
11764
11765   /* for backwards compatibility, the following code emulates a fixed bug that
11766      occured when pushing elements (causing elements that just made their last
11767      pushing step to already (if possible) make their first falling step in the
11768      same game frame, which is bad); this code is also needed to use the famous
11769      "spring push bug" which is used in older levels and might be wanted to be
11770      used also in newer levels, but in this case the buggy pushing code is only
11771      affecting the "spring" element and no other elements */
11772
11773   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11774   {
11775     for (i = 0; i < MAX_PLAYERS; i++)
11776     {
11777       struct PlayerInfo *player = &stored_player[i];
11778       int x = player->jx;
11779       int y = player->jy;
11780
11781       if (player->active && player->is_pushing && player->is_moving &&
11782           IS_MOVING(x, y) &&
11783           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11784            Feld[x][y] == EL_SPRING))
11785       {
11786         ContinueMoving(x, y);
11787
11788         // continue moving after pushing (this is actually a bug)
11789         if (!IS_MOVING(x, y))
11790           Stop[x][y] = FALSE;
11791       }
11792     }
11793   }
11794
11795   SCAN_PLAYFIELD(x, y)
11796   {
11797     Last[x][y] = Feld[x][y];
11798
11799     ChangeCount[x][y] = 0;
11800     ChangeEvent[x][y] = -1;
11801
11802     // this must be handled before main playfield loop
11803     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11804     {
11805       MovDelay[x][y]--;
11806       if (MovDelay[x][y] <= 0)
11807         RemoveField(x, y);
11808     }
11809
11810     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11811     {
11812       MovDelay[x][y]--;
11813       if (MovDelay[x][y] <= 0)
11814       {
11815         RemoveField(x, y);
11816         TEST_DrawLevelField(x, y);
11817
11818         TestIfElementTouchesCustomElement(x, y);        // for empty space
11819       }
11820     }
11821
11822 #if DEBUG
11823     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11824     {
11825       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11826       printf("GameActions(): This should never happen!\n");
11827
11828       ChangePage[x][y] = -1;
11829     }
11830 #endif
11831
11832     Stop[x][y] = FALSE;
11833     if (WasJustMoving[x][y] > 0)
11834       WasJustMoving[x][y]--;
11835     if (WasJustFalling[x][y] > 0)
11836       WasJustFalling[x][y]--;
11837     if (CheckCollision[x][y] > 0)
11838       CheckCollision[x][y]--;
11839     if (CheckImpact[x][y] > 0)
11840       CheckImpact[x][y]--;
11841
11842     GfxFrame[x][y]++;
11843
11844     /* reset finished pushing action (not done in ContinueMoving() to allow
11845        continuous pushing animation for elements with zero push delay) */
11846     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11847     {
11848       ResetGfxAnimation(x, y);
11849       TEST_DrawLevelField(x, y);
11850     }
11851
11852 #if DEBUG
11853     if (IS_BLOCKED(x, y))
11854     {
11855       int oldx, oldy;
11856
11857       Blocked2Moving(x, y, &oldx, &oldy);
11858       if (!IS_MOVING(oldx, oldy))
11859       {
11860         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11861         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11862         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11863         printf("GameActions(): This should never happen!\n");
11864       }
11865     }
11866 #endif
11867   }
11868
11869   SCAN_PLAYFIELD(x, y)
11870   {
11871     element = Feld[x][y];
11872     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11873     last_gfx_frame = GfxFrame[x][y];
11874
11875     ResetGfxFrame(x, y);
11876
11877     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11878       DrawLevelGraphicAnimation(x, y, graphic);
11879
11880     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11881         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11882       ResetRandomAnimationValue(x, y);
11883
11884     SetRandomAnimationValue(x, y);
11885
11886     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11887
11888     if (IS_INACTIVE(element))
11889     {
11890       if (IS_ANIMATED(graphic))
11891         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11892
11893       continue;
11894     }
11895
11896     // this may take place after moving, so 'element' may have changed
11897     if (IS_CHANGING(x, y) &&
11898         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11899     {
11900       int page = element_info[element].event_page_nr[CE_DELAY];
11901
11902       HandleElementChange(x, y, page);
11903
11904       element = Feld[x][y];
11905       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11906     }
11907
11908     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11909     {
11910       StartMoving(x, y);
11911
11912       element = Feld[x][y];
11913       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11914
11915       if (IS_ANIMATED(graphic) &&
11916           !IS_MOVING(x, y) &&
11917           !Stop[x][y])
11918         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11919
11920       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11921         TEST_DrawTwinkleOnField(x, y);
11922     }
11923     else if (element == EL_ACID)
11924     {
11925       if (!Stop[x][y])
11926         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11927     }
11928     else if ((element == EL_EXIT_OPEN ||
11929               element == EL_EM_EXIT_OPEN ||
11930               element == EL_SP_EXIT_OPEN ||
11931               element == EL_STEEL_EXIT_OPEN ||
11932               element == EL_EM_STEEL_EXIT_OPEN ||
11933               element == EL_SP_TERMINAL ||
11934               element == EL_SP_TERMINAL_ACTIVE ||
11935               element == EL_EXTRA_TIME ||
11936               element == EL_SHIELD_NORMAL ||
11937               element == EL_SHIELD_DEADLY) &&
11938              IS_ANIMATED(graphic))
11939       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11940     else if (IS_MOVING(x, y))
11941       ContinueMoving(x, y);
11942     else if (IS_ACTIVE_BOMB(element))
11943       CheckDynamite(x, y);
11944     else if (element == EL_AMOEBA_GROWING)
11945       AmoebeWaechst(x, y);
11946     else if (element == EL_AMOEBA_SHRINKING)
11947       AmoebaDisappearing(x, y);
11948
11949 #if !USE_NEW_AMOEBA_CODE
11950     else if (IS_AMOEBALIVE(element))
11951       AmoebeAbleger(x, y);
11952 #endif
11953
11954     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11955       Life(x, y);
11956     else if (element == EL_EXIT_CLOSED)
11957       CheckExit(x, y);
11958     else if (element == EL_EM_EXIT_CLOSED)
11959       CheckExitEM(x, y);
11960     else if (element == EL_STEEL_EXIT_CLOSED)
11961       CheckExitSteel(x, y);
11962     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11963       CheckExitSteelEM(x, y);
11964     else if (element == EL_SP_EXIT_CLOSED)
11965       CheckExitSP(x, y);
11966     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11967              element == EL_EXPANDABLE_STEELWALL_GROWING)
11968       MauerWaechst(x, y);
11969     else if (element == EL_EXPANDABLE_WALL ||
11970              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11971              element == EL_EXPANDABLE_WALL_VERTICAL ||
11972              element == EL_EXPANDABLE_WALL_ANY ||
11973              element == EL_BD_EXPANDABLE_WALL)
11974       MauerAbleger(x, y);
11975     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11976              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11977              element == EL_EXPANDABLE_STEELWALL_ANY)
11978       MauerAblegerStahl(x, y);
11979     else if (element == EL_FLAMES)
11980       CheckForDragon(x, y);
11981     else if (element == EL_EXPLOSION)
11982       ; // drawing of correct explosion animation is handled separately
11983     else if (element == EL_ELEMENT_SNAPPING ||
11984              element == EL_DIAGONAL_SHRINKING ||
11985              element == EL_DIAGONAL_GROWING)
11986     {
11987       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11988
11989       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11990     }
11991     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11992       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11993
11994     if (IS_BELT_ACTIVE(element))
11995       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11996
11997     if (game.magic_wall_active)
11998     {
11999       int jx = local_player->jx, jy = local_player->jy;
12000
12001       // play the element sound at the position nearest to the player
12002       if ((element == EL_MAGIC_WALL_FULL ||
12003            element == EL_MAGIC_WALL_ACTIVE ||
12004            element == EL_MAGIC_WALL_EMPTYING ||
12005            element == EL_BD_MAGIC_WALL_FULL ||
12006            element == EL_BD_MAGIC_WALL_ACTIVE ||
12007            element == EL_BD_MAGIC_WALL_EMPTYING ||
12008            element == EL_DC_MAGIC_WALL_FULL ||
12009            element == EL_DC_MAGIC_WALL_ACTIVE ||
12010            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12011           ABS(x - jx) + ABS(y - jy) <
12012           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12013       {
12014         magic_wall_x = x;
12015         magic_wall_y = y;
12016       }
12017     }
12018   }
12019
12020 #if USE_NEW_AMOEBA_CODE
12021   // new experimental amoeba growth stuff
12022   if (!(FrameCounter % 8))
12023   {
12024     static unsigned int random = 1684108901;
12025
12026     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12027     {
12028       x = RND(lev_fieldx);
12029       y = RND(lev_fieldy);
12030       element = Feld[x][y];
12031
12032       if (!IS_PLAYER(x,y) &&
12033           (element == EL_EMPTY ||
12034            CAN_GROW_INTO(element) ||
12035            element == EL_QUICKSAND_EMPTY ||
12036            element == EL_QUICKSAND_FAST_EMPTY ||
12037            element == EL_ACID_SPLASH_LEFT ||
12038            element == EL_ACID_SPLASH_RIGHT))
12039       {
12040         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12041             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12042             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12043             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12044           Feld[x][y] = EL_AMOEBA_DROP;
12045       }
12046
12047       random = random * 129 + 1;
12048     }
12049   }
12050 #endif
12051
12052   game.explosions_delayed = FALSE;
12053
12054   SCAN_PLAYFIELD(x, y)
12055   {
12056     element = Feld[x][y];
12057
12058     if (ExplodeField[x][y])
12059       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12060     else if (element == EL_EXPLOSION)
12061       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12062
12063     ExplodeField[x][y] = EX_TYPE_NONE;
12064   }
12065
12066   game.explosions_delayed = TRUE;
12067
12068   if (game.magic_wall_active)
12069   {
12070     if (!(game.magic_wall_time_left % 4))
12071     {
12072       int element = Feld[magic_wall_x][magic_wall_y];
12073
12074       if (element == EL_BD_MAGIC_WALL_FULL ||
12075           element == EL_BD_MAGIC_WALL_ACTIVE ||
12076           element == EL_BD_MAGIC_WALL_EMPTYING)
12077         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12078       else if (element == EL_DC_MAGIC_WALL_FULL ||
12079                element == EL_DC_MAGIC_WALL_ACTIVE ||
12080                element == EL_DC_MAGIC_WALL_EMPTYING)
12081         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12082       else
12083         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12084     }
12085
12086     if (game.magic_wall_time_left > 0)
12087     {
12088       game.magic_wall_time_left--;
12089
12090       if (!game.magic_wall_time_left)
12091       {
12092         SCAN_PLAYFIELD(x, y)
12093         {
12094           element = Feld[x][y];
12095
12096           if (element == EL_MAGIC_WALL_ACTIVE ||
12097               element == EL_MAGIC_WALL_FULL)
12098           {
12099             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12100             TEST_DrawLevelField(x, y);
12101           }
12102           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12103                    element == EL_BD_MAGIC_WALL_FULL)
12104           {
12105             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12106             TEST_DrawLevelField(x, y);
12107           }
12108           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12109                    element == EL_DC_MAGIC_WALL_FULL)
12110           {
12111             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12112             TEST_DrawLevelField(x, y);
12113           }
12114         }
12115
12116         game.magic_wall_active = FALSE;
12117       }
12118     }
12119   }
12120
12121   if (game.light_time_left > 0)
12122   {
12123     game.light_time_left--;
12124
12125     if (game.light_time_left == 0)
12126       RedrawAllLightSwitchesAndInvisibleElements();
12127   }
12128
12129   if (game.timegate_time_left > 0)
12130   {
12131     game.timegate_time_left--;
12132
12133     if (game.timegate_time_left == 0)
12134       CloseAllOpenTimegates();
12135   }
12136
12137   if (game.lenses_time_left > 0)
12138   {
12139     game.lenses_time_left--;
12140
12141     if (game.lenses_time_left == 0)
12142       RedrawAllInvisibleElementsForLenses();
12143   }
12144
12145   if (game.magnify_time_left > 0)
12146   {
12147     game.magnify_time_left--;
12148
12149     if (game.magnify_time_left == 0)
12150       RedrawAllInvisibleElementsForMagnifier();
12151   }
12152
12153   for (i = 0; i < MAX_PLAYERS; i++)
12154   {
12155     struct PlayerInfo *player = &stored_player[i];
12156
12157     if (SHIELD_ON(player))
12158     {
12159       if (player->shield_deadly_time_left)
12160         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12161       else if (player->shield_normal_time_left)
12162         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12163     }
12164   }
12165
12166 #if USE_DELAYED_GFX_REDRAW
12167   SCAN_PLAYFIELD(x, y)
12168   {
12169     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12170     {
12171       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12172          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12173
12174       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12175         DrawLevelField(x, y);
12176
12177       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12178         DrawLevelFieldCrumbled(x, y);
12179
12180       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12181         DrawLevelFieldCrumbledNeighbours(x, y);
12182
12183       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12184         DrawTwinkleOnField(x, y);
12185     }
12186
12187     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12188   }
12189 #endif
12190
12191   DrawAllPlayers();
12192   PlayAllPlayersSound();
12193
12194   for (i = 0; i < MAX_PLAYERS; i++)
12195   {
12196     struct PlayerInfo *player = &stored_player[i];
12197
12198     if (player->show_envelope != 0 && (!player->active ||
12199                                        player->MovPos == 0))
12200     {
12201       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12202
12203       player->show_envelope = 0;
12204     }
12205   }
12206
12207   // use random number generator in every frame to make it less predictable
12208   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12209     RND(1);
12210 }
12211
12212 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12213 {
12214   int min_x = x, min_y = y, max_x = x, max_y = y;
12215   int i;
12216
12217   for (i = 0; i < MAX_PLAYERS; i++)
12218   {
12219     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12220
12221     if (!stored_player[i].active || &stored_player[i] == player)
12222       continue;
12223
12224     min_x = MIN(min_x, jx);
12225     min_y = MIN(min_y, jy);
12226     max_x = MAX(max_x, jx);
12227     max_y = MAX(max_y, jy);
12228   }
12229
12230   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12231 }
12232
12233 static boolean AllPlayersInVisibleScreen(void)
12234 {
12235   int i;
12236
12237   for (i = 0; i < MAX_PLAYERS; i++)
12238   {
12239     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12240
12241     if (!stored_player[i].active)
12242       continue;
12243
12244     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12245       return FALSE;
12246   }
12247
12248   return TRUE;
12249 }
12250
12251 void ScrollLevel(int dx, int dy)
12252 {
12253   int scroll_offset = 2 * TILEX_VAR;
12254   int x, y;
12255
12256   BlitBitmap(drawto_field, drawto_field,
12257              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12258              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12259              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12260              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12261              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12262              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12263
12264   if (dx != 0)
12265   {
12266     x = (dx == 1 ? BX1 : BX2);
12267     for (y = BY1; y <= BY2; y++)
12268       DrawScreenField(x, y);
12269   }
12270
12271   if (dy != 0)
12272   {
12273     y = (dy == 1 ? BY1 : BY2);
12274     for (x = BX1; x <= BX2; x++)
12275       DrawScreenField(x, y);
12276   }
12277
12278   redraw_mask |= REDRAW_FIELD;
12279 }
12280
12281 static boolean canFallDown(struct PlayerInfo *player)
12282 {
12283   int jx = player->jx, jy = player->jy;
12284
12285   return (IN_LEV_FIELD(jx, jy + 1) &&
12286           (IS_FREE(jx, jy + 1) ||
12287            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12288           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12289           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12290 }
12291
12292 static boolean canPassField(int x, int y, int move_dir)
12293 {
12294   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12295   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12296   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12297   int nextx = x + dx;
12298   int nexty = y + dy;
12299   int element = Feld[x][y];
12300
12301   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12302           !CAN_MOVE(element) &&
12303           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12304           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12305           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12306 }
12307
12308 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12309 {
12310   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12311   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12312   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12313   int newx = x + dx;
12314   int newy = y + dy;
12315
12316   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12317           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12318           (IS_DIGGABLE(Feld[newx][newy]) ||
12319            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12320            canPassField(newx, newy, move_dir)));
12321 }
12322
12323 static void CheckGravityMovement(struct PlayerInfo *player)
12324 {
12325   if (player->gravity && !player->programmed_action)
12326   {
12327     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12328     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12329     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12330     int jx = player->jx, jy = player->jy;
12331     boolean player_is_moving_to_valid_field =
12332       (!player_is_snapping &&
12333        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12334         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12335     boolean player_can_fall_down = canFallDown(player);
12336
12337     if (player_can_fall_down &&
12338         !player_is_moving_to_valid_field)
12339       player->programmed_action = MV_DOWN;
12340   }
12341 }
12342
12343 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12344 {
12345   return CheckGravityMovement(player);
12346
12347   if (player->gravity && !player->programmed_action)
12348   {
12349     int jx = player->jx, jy = player->jy;
12350     boolean field_under_player_is_free =
12351       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12352     boolean player_is_standing_on_valid_field =
12353       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12354        (IS_WALKABLE(Feld[jx][jy]) &&
12355         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12356
12357     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12358       player->programmed_action = MV_DOWN;
12359   }
12360 }
12361
12362 /*
12363   MovePlayerOneStep()
12364   -----------------------------------------------------------------------------
12365   dx, dy:               direction (non-diagonal) to try to move the player to
12366   real_dx, real_dy:     direction as read from input device (can be diagonal)
12367 */
12368
12369 boolean MovePlayerOneStep(struct PlayerInfo *player,
12370                           int dx, int dy, int real_dx, int real_dy)
12371 {
12372   int jx = player->jx, jy = player->jy;
12373   int new_jx = jx + dx, new_jy = jy + dy;
12374   int can_move;
12375   boolean player_can_move = !player->cannot_move;
12376
12377   if (!player->active || (!dx && !dy))
12378     return MP_NO_ACTION;
12379
12380   player->MovDir = (dx < 0 ? MV_LEFT :
12381                     dx > 0 ? MV_RIGHT :
12382                     dy < 0 ? MV_UP :
12383                     dy > 0 ? MV_DOWN :  MV_NONE);
12384
12385   if (!IN_LEV_FIELD(new_jx, new_jy))
12386     return MP_NO_ACTION;
12387
12388   if (!player_can_move)
12389   {
12390     if (player->MovPos == 0)
12391     {
12392       player->is_moving = FALSE;
12393       player->is_digging = FALSE;
12394       player->is_collecting = FALSE;
12395       player->is_snapping = FALSE;
12396       player->is_pushing = FALSE;
12397     }
12398   }
12399
12400   if (!network.enabled && game.centered_player_nr == -1 &&
12401       !AllPlayersInSight(player, new_jx, new_jy))
12402     return MP_NO_ACTION;
12403
12404   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12405   if (can_move != MP_MOVING)
12406     return can_move;
12407
12408   // check if DigField() has caused relocation of the player
12409   if (player->jx != jx || player->jy != jy)
12410     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12411
12412   StorePlayer[jx][jy] = 0;
12413   player->last_jx = jx;
12414   player->last_jy = jy;
12415   player->jx = new_jx;
12416   player->jy = new_jy;
12417   StorePlayer[new_jx][new_jy] = player->element_nr;
12418
12419   if (player->move_delay_value_next != -1)
12420   {
12421     player->move_delay_value = player->move_delay_value_next;
12422     player->move_delay_value_next = -1;
12423   }
12424
12425   player->MovPos =
12426     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12427
12428   player->step_counter++;
12429
12430   PlayerVisit[jx][jy] = FrameCounter;
12431
12432   player->is_moving = TRUE;
12433
12434 #if 1
12435   // should better be called in MovePlayer(), but this breaks some tapes
12436   ScrollPlayer(player, SCROLL_INIT);
12437 #endif
12438
12439   return MP_MOVING;
12440 }
12441
12442 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12443 {
12444   int jx = player->jx, jy = player->jy;
12445   int old_jx = jx, old_jy = jy;
12446   int moved = MP_NO_ACTION;
12447
12448   if (!player->active)
12449     return FALSE;
12450
12451   if (!dx && !dy)
12452   {
12453     if (player->MovPos == 0)
12454     {
12455       player->is_moving = FALSE;
12456       player->is_digging = FALSE;
12457       player->is_collecting = FALSE;
12458       player->is_snapping = FALSE;
12459       player->is_pushing = FALSE;
12460     }
12461
12462     return FALSE;
12463   }
12464
12465   if (player->move_delay > 0)
12466     return FALSE;
12467
12468   player->move_delay = -1;              // set to "uninitialized" value
12469
12470   // store if player is automatically moved to next field
12471   player->is_auto_moving = (player->programmed_action != MV_NONE);
12472
12473   // remove the last programmed player action
12474   player->programmed_action = 0;
12475
12476   if (player->MovPos)
12477   {
12478     // should only happen if pre-1.2 tape recordings are played
12479     // this is only for backward compatibility
12480
12481     int original_move_delay_value = player->move_delay_value;
12482
12483 #if DEBUG
12484     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12485            tape.counter);
12486 #endif
12487
12488     // scroll remaining steps with finest movement resolution
12489     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12490
12491     while (player->MovPos)
12492     {
12493       ScrollPlayer(player, SCROLL_GO_ON);
12494       ScrollScreen(NULL, SCROLL_GO_ON);
12495
12496       AdvanceFrameAndPlayerCounters(player->index_nr);
12497
12498       DrawAllPlayers();
12499       BackToFront_WithFrameDelay(0);
12500     }
12501
12502     player->move_delay_value = original_move_delay_value;
12503   }
12504
12505   player->is_active = FALSE;
12506
12507   if (player->last_move_dir & MV_HORIZONTAL)
12508   {
12509     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12510       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12511   }
12512   else
12513   {
12514     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12515       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12516   }
12517
12518   if (!moved && !player->is_active)
12519   {
12520     player->is_moving = FALSE;
12521     player->is_digging = FALSE;
12522     player->is_collecting = FALSE;
12523     player->is_snapping = FALSE;
12524     player->is_pushing = FALSE;
12525   }
12526
12527   jx = player->jx;
12528   jy = player->jy;
12529
12530   if (moved & MP_MOVING && !ScreenMovPos &&
12531       (player->index_nr == game.centered_player_nr ||
12532        game.centered_player_nr == -1))
12533   {
12534     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12535
12536     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12537     {
12538       // actual player has left the screen -- scroll in that direction
12539       if (jx != old_jx)         // player has moved horizontally
12540         scroll_x += (jx - old_jx);
12541       else                      // player has moved vertically
12542         scroll_y += (jy - old_jy);
12543     }
12544     else
12545     {
12546       int offset_raw = game.scroll_delay_value;
12547
12548       if (jx != old_jx)         // player has moved horizontally
12549       {
12550         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12551         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12552         int new_scroll_x = jx - MIDPOSX + offset_x;
12553
12554         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12555             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12556           scroll_x = new_scroll_x;
12557
12558         // don't scroll over playfield boundaries
12559         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12560
12561         // don't scroll more than one field at a time
12562         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12563
12564         // don't scroll against the player's moving direction
12565         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12566             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12567           scroll_x = old_scroll_x;
12568       }
12569       else                      // player has moved vertically
12570       {
12571         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12572         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12573         int new_scroll_y = jy - MIDPOSY + offset_y;
12574
12575         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12576             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12577           scroll_y = new_scroll_y;
12578
12579         // don't scroll over playfield boundaries
12580         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12581
12582         // don't scroll more than one field at a time
12583         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12584
12585         // don't scroll against the player's moving direction
12586         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12587             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12588           scroll_y = old_scroll_y;
12589       }
12590     }
12591
12592     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12593     {
12594       if (!network.enabled && game.centered_player_nr == -1 &&
12595           !AllPlayersInVisibleScreen())
12596       {
12597         scroll_x = old_scroll_x;
12598         scroll_y = old_scroll_y;
12599       }
12600       else
12601       {
12602         ScrollScreen(player, SCROLL_INIT);
12603         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12604       }
12605     }
12606   }
12607
12608   player->StepFrame = 0;
12609
12610   if (moved & MP_MOVING)
12611   {
12612     if (old_jx != jx && old_jy == jy)
12613       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12614     else if (old_jx == jx && old_jy != jy)
12615       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12616
12617     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12618
12619     player->last_move_dir = player->MovDir;
12620     player->is_moving = TRUE;
12621     player->is_snapping = FALSE;
12622     player->is_switching = FALSE;
12623     player->is_dropping = FALSE;
12624     player->is_dropping_pressed = FALSE;
12625     player->drop_pressed_delay = 0;
12626
12627 #if 0
12628     // should better be called here than above, but this breaks some tapes
12629     ScrollPlayer(player, SCROLL_INIT);
12630 #endif
12631   }
12632   else
12633   {
12634     CheckGravityMovementWhenNotMoving(player);
12635
12636     player->is_moving = FALSE;
12637
12638     /* at this point, the player is allowed to move, but cannot move right now
12639        (e.g. because of something blocking the way) -- ensure that the player
12640        is also allowed to move in the next frame (in old versions before 3.1.1,
12641        the player was forced to wait again for eight frames before next try) */
12642
12643     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12644       player->move_delay = 0;   // allow direct movement in the next frame
12645   }
12646
12647   if (player->move_delay == -1)         // not yet initialized by DigField()
12648     player->move_delay = player->move_delay_value;
12649
12650   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12651   {
12652     TestIfPlayerTouchesBadThing(jx, jy);
12653     TestIfPlayerTouchesCustomElement(jx, jy);
12654   }
12655
12656   if (!player->active)
12657     RemovePlayer(player);
12658
12659   return moved;
12660 }
12661
12662 void ScrollPlayer(struct PlayerInfo *player, int mode)
12663 {
12664   int jx = player->jx, jy = player->jy;
12665   int last_jx = player->last_jx, last_jy = player->last_jy;
12666   int move_stepsize = TILEX / player->move_delay_value;
12667
12668   if (!player->active)
12669     return;
12670
12671   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12672     return;
12673
12674   if (mode == SCROLL_INIT)
12675   {
12676     player->actual_frame_counter = FrameCounter;
12677     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12678
12679     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12680         Feld[last_jx][last_jy] == EL_EMPTY)
12681     {
12682       int last_field_block_delay = 0;   // start with no blocking at all
12683       int block_delay_adjustment = player->block_delay_adjustment;
12684
12685       // if player blocks last field, add delay for exactly one move
12686       if (player->block_last_field)
12687       {
12688         last_field_block_delay += player->move_delay_value;
12689
12690         // when blocking enabled, prevent moving up despite gravity
12691         if (player->gravity && player->MovDir == MV_UP)
12692           block_delay_adjustment = -1;
12693       }
12694
12695       // add block delay adjustment (also possible when not blocking)
12696       last_field_block_delay += block_delay_adjustment;
12697
12698       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12699       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12700     }
12701
12702     if (player->MovPos != 0)    // player has not yet reached destination
12703       return;
12704   }
12705   else if (!FrameReached(&player->actual_frame_counter, 1))
12706     return;
12707
12708   if (player->MovPos != 0)
12709   {
12710     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12711     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12712
12713     // before DrawPlayer() to draw correct player graphic for this case
12714     if (player->MovPos == 0)
12715       CheckGravityMovement(player);
12716   }
12717
12718   if (player->MovPos == 0)      // player reached destination field
12719   {
12720     if (player->move_delay_reset_counter > 0)
12721     {
12722       player->move_delay_reset_counter--;
12723
12724       if (player->move_delay_reset_counter == 0)
12725       {
12726         // continue with normal speed after quickly moving through gate
12727         HALVE_PLAYER_SPEED(player);
12728
12729         // be able to make the next move without delay
12730         player->move_delay = 0;
12731       }
12732     }
12733
12734     player->last_jx = jx;
12735     player->last_jy = jy;
12736
12737     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12738         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12739         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12740         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12741         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12742         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12743         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12744         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12745     {
12746       ExitPlayer(player);
12747
12748       if (game.players_still_needed == 0 &&
12749           (game.friends_still_needed == 0 ||
12750            IS_SP_ELEMENT(Feld[jx][jy])))
12751         LevelSolved();
12752     }
12753
12754     // this breaks one level: "machine", level 000
12755     {
12756       int move_direction = player->MovDir;
12757       int enter_side = MV_DIR_OPPOSITE(move_direction);
12758       int leave_side = move_direction;
12759       int old_jx = last_jx;
12760       int old_jy = last_jy;
12761       int old_element = Feld[old_jx][old_jy];
12762       int new_element = Feld[jx][jy];
12763
12764       if (IS_CUSTOM_ELEMENT(old_element))
12765         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12766                                    CE_LEFT_BY_PLAYER,
12767                                    player->index_bit, leave_side);
12768
12769       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12770                                           CE_PLAYER_LEAVES_X,
12771                                           player->index_bit, leave_side);
12772
12773       if (IS_CUSTOM_ELEMENT(new_element))
12774         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12775                                    player->index_bit, enter_side);
12776
12777       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12778                                           CE_PLAYER_ENTERS_X,
12779                                           player->index_bit, enter_side);
12780
12781       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12782                                         CE_MOVE_OF_X, move_direction);
12783     }
12784
12785     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12786     {
12787       TestIfPlayerTouchesBadThing(jx, jy);
12788       TestIfPlayerTouchesCustomElement(jx, jy);
12789
12790       /* needed because pushed element has not yet reached its destination,
12791          so it would trigger a change event at its previous field location */
12792       if (!player->is_pushing)
12793         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12794
12795       if (!player->active)
12796         RemovePlayer(player);
12797     }
12798
12799     if (!game.LevelSolved && level.use_step_counter)
12800     {
12801       int i;
12802
12803       TimePlayed++;
12804
12805       if (TimeLeft > 0)
12806       {
12807         TimeLeft--;
12808
12809         if (TimeLeft <= 10 && setup.time_limit)
12810           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12811
12812         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12813
12814         DisplayGameControlValues();
12815
12816         if (!TimeLeft && setup.time_limit)
12817           for (i = 0; i < MAX_PLAYERS; i++)
12818             KillPlayer(&stored_player[i]);
12819       }
12820       else if (game.no_time_limit && !game.all_players_gone)
12821       {
12822         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12823
12824         DisplayGameControlValues();
12825       }
12826     }
12827
12828     if (tape.single_step && tape.recording && !tape.pausing &&
12829         !player->programmed_action)
12830       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12831
12832     if (!player->programmed_action)
12833       CheckSaveEngineSnapshot(player);
12834   }
12835 }
12836
12837 void ScrollScreen(struct PlayerInfo *player, int mode)
12838 {
12839   static unsigned int screen_frame_counter = 0;
12840
12841   if (mode == SCROLL_INIT)
12842   {
12843     // set scrolling step size according to actual player's moving speed
12844     ScrollStepSize = TILEX / player->move_delay_value;
12845
12846     screen_frame_counter = FrameCounter;
12847     ScreenMovDir = player->MovDir;
12848     ScreenMovPos = player->MovPos;
12849     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12850     return;
12851   }
12852   else if (!FrameReached(&screen_frame_counter, 1))
12853     return;
12854
12855   if (ScreenMovPos)
12856   {
12857     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12858     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12859     redraw_mask |= REDRAW_FIELD;
12860   }
12861   else
12862     ScreenMovDir = MV_NONE;
12863 }
12864
12865 void TestIfPlayerTouchesCustomElement(int x, int y)
12866 {
12867   static int xy[4][2] =
12868   {
12869     { 0, -1 },
12870     { -1, 0 },
12871     { +1, 0 },
12872     { 0, +1 }
12873   };
12874   static int trigger_sides[4][2] =
12875   {
12876     // center side       border side
12877     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12878     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12879     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12880     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12881   };
12882   static int touch_dir[4] =
12883   {
12884     MV_LEFT | MV_RIGHT,
12885     MV_UP   | MV_DOWN,
12886     MV_UP   | MV_DOWN,
12887     MV_LEFT | MV_RIGHT
12888   };
12889   int center_element = Feld[x][y];      // should always be non-moving!
12890   int i;
12891
12892   for (i = 0; i < NUM_DIRECTIONS; i++)
12893   {
12894     int xx = x + xy[i][0];
12895     int yy = y + xy[i][1];
12896     int center_side = trigger_sides[i][0];
12897     int border_side = trigger_sides[i][1];
12898     int border_element;
12899
12900     if (!IN_LEV_FIELD(xx, yy))
12901       continue;
12902
12903     if (IS_PLAYER(x, y))                // player found at center element
12904     {
12905       struct PlayerInfo *player = PLAYERINFO(x, y);
12906
12907       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12908         border_element = Feld[xx][yy];          // may be moving!
12909       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12910         border_element = Feld[xx][yy];
12911       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12912         border_element = MovingOrBlocked2Element(xx, yy);
12913       else
12914         continue;               // center and border element do not touch
12915
12916       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12917                                  player->index_bit, border_side);
12918       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12919                                           CE_PLAYER_TOUCHES_X,
12920                                           player->index_bit, border_side);
12921
12922       {
12923         /* use player element that is initially defined in the level playfield,
12924            not the player element that corresponds to the runtime player number
12925            (example: a level that contains EL_PLAYER_3 as the only player would
12926            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12927         int player_element = PLAYERINFO(x, y)->initial_element;
12928
12929         CheckElementChangeBySide(xx, yy, border_element, player_element,
12930                                  CE_TOUCHING_X, border_side);
12931       }
12932     }
12933     else if (IS_PLAYER(xx, yy))         // player found at border element
12934     {
12935       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12936
12937       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12938       {
12939         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12940           continue;             // center and border element do not touch
12941       }
12942
12943       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12944                                  player->index_bit, center_side);
12945       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12946                                           CE_PLAYER_TOUCHES_X,
12947                                           player->index_bit, center_side);
12948
12949       {
12950         /* use player element that is initially defined in the level playfield,
12951            not the player element that corresponds to the runtime player number
12952            (example: a level that contains EL_PLAYER_3 as the only player would
12953            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12954         int player_element = PLAYERINFO(xx, yy)->initial_element;
12955
12956         CheckElementChangeBySide(x, y, center_element, player_element,
12957                                  CE_TOUCHING_X, center_side);
12958       }
12959
12960       break;
12961     }
12962   }
12963 }
12964
12965 void TestIfElementTouchesCustomElement(int x, int y)
12966 {
12967   static int xy[4][2] =
12968   {
12969     { 0, -1 },
12970     { -1, 0 },
12971     { +1, 0 },
12972     { 0, +1 }
12973   };
12974   static int trigger_sides[4][2] =
12975   {
12976     // center side      border side
12977     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12978     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12979     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12980     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12981   };
12982   static int touch_dir[4] =
12983   {
12984     MV_LEFT | MV_RIGHT,
12985     MV_UP   | MV_DOWN,
12986     MV_UP   | MV_DOWN,
12987     MV_LEFT | MV_RIGHT
12988   };
12989   boolean change_center_element = FALSE;
12990   int center_element = Feld[x][y];      // should always be non-moving!
12991   int border_element_old[NUM_DIRECTIONS];
12992   int i;
12993
12994   for (i = 0; i < NUM_DIRECTIONS; i++)
12995   {
12996     int xx = x + xy[i][0];
12997     int yy = y + xy[i][1];
12998     int border_element;
12999
13000     border_element_old[i] = -1;
13001
13002     if (!IN_LEV_FIELD(xx, yy))
13003       continue;
13004
13005     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13006       border_element = Feld[xx][yy];    // may be moving!
13007     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13008       border_element = Feld[xx][yy];
13009     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13010       border_element = MovingOrBlocked2Element(xx, yy);
13011     else
13012       continue;                 // center and border element do not touch
13013
13014     border_element_old[i] = border_element;
13015   }
13016
13017   for (i = 0; i < NUM_DIRECTIONS; i++)
13018   {
13019     int xx = x + xy[i][0];
13020     int yy = y + xy[i][1];
13021     int center_side = trigger_sides[i][0];
13022     int border_element = border_element_old[i];
13023
13024     if (border_element == -1)
13025       continue;
13026
13027     // check for change of border element
13028     CheckElementChangeBySide(xx, yy, border_element, center_element,
13029                              CE_TOUCHING_X, center_side);
13030
13031     // (center element cannot be player, so we dont have to check this here)
13032   }
13033
13034   for (i = 0; i < NUM_DIRECTIONS; i++)
13035   {
13036     int xx = x + xy[i][0];
13037     int yy = y + xy[i][1];
13038     int border_side = trigger_sides[i][1];
13039     int border_element = border_element_old[i];
13040
13041     if (border_element == -1)
13042       continue;
13043
13044     // check for change of center element (but change it only once)
13045     if (!change_center_element)
13046       change_center_element =
13047         CheckElementChangeBySide(x, y, center_element, border_element,
13048                                  CE_TOUCHING_X, border_side);
13049
13050     if (IS_PLAYER(xx, yy))
13051     {
13052       /* use player element that is initially defined in the level playfield,
13053          not the player element that corresponds to the runtime player number
13054          (example: a level that contains EL_PLAYER_3 as the only player would
13055          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13056       int player_element = PLAYERINFO(xx, yy)->initial_element;
13057
13058       CheckElementChangeBySide(x, y, center_element, player_element,
13059                                CE_TOUCHING_X, border_side);
13060     }
13061   }
13062 }
13063
13064 void TestIfElementHitsCustomElement(int x, int y, int direction)
13065 {
13066   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13067   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13068   int hitx = x + dx, hity = y + dy;
13069   int hitting_element = Feld[x][y];
13070   int touched_element;
13071
13072   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13073     return;
13074
13075   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13076                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13077
13078   if (IN_LEV_FIELD(hitx, hity))
13079   {
13080     int opposite_direction = MV_DIR_OPPOSITE(direction);
13081     int hitting_side = direction;
13082     int touched_side = opposite_direction;
13083     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13084                           MovDir[hitx][hity] != direction ||
13085                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13086
13087     object_hit = TRUE;
13088
13089     if (object_hit)
13090     {
13091       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13092                                CE_HITTING_X, touched_side);
13093
13094       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13095                                CE_HIT_BY_X, hitting_side);
13096
13097       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13098                                CE_HIT_BY_SOMETHING, opposite_direction);
13099
13100       if (IS_PLAYER(hitx, hity))
13101       {
13102         /* use player element that is initially defined in the level playfield,
13103            not the player element that corresponds to the runtime player number
13104            (example: a level that contains EL_PLAYER_3 as the only player would
13105            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13106         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13107
13108         CheckElementChangeBySide(x, y, hitting_element, player_element,
13109                                  CE_HITTING_X, touched_side);
13110       }
13111     }
13112   }
13113
13114   // "hitting something" is also true when hitting the playfield border
13115   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13116                            CE_HITTING_SOMETHING, direction);
13117 }
13118
13119 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13120 {
13121   int i, kill_x = -1, kill_y = -1;
13122
13123   int bad_element = -1;
13124   static int test_xy[4][2] =
13125   {
13126     { 0, -1 },
13127     { -1, 0 },
13128     { +1, 0 },
13129     { 0, +1 }
13130   };
13131   static int test_dir[4] =
13132   {
13133     MV_UP,
13134     MV_LEFT,
13135     MV_RIGHT,
13136     MV_DOWN
13137   };
13138
13139   for (i = 0; i < NUM_DIRECTIONS; i++)
13140   {
13141     int test_x, test_y, test_move_dir, test_element;
13142
13143     test_x = good_x + test_xy[i][0];
13144     test_y = good_y + test_xy[i][1];
13145
13146     if (!IN_LEV_FIELD(test_x, test_y))
13147       continue;
13148
13149     test_move_dir =
13150       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13151
13152     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13153
13154     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13155        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13156     */
13157     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13158         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13159     {
13160       kill_x = test_x;
13161       kill_y = test_y;
13162       bad_element = test_element;
13163
13164       break;
13165     }
13166   }
13167
13168   if (kill_x != -1 || kill_y != -1)
13169   {
13170     if (IS_PLAYER(good_x, good_y))
13171     {
13172       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13173
13174       if (player->shield_deadly_time_left > 0 &&
13175           !IS_INDESTRUCTIBLE(bad_element))
13176         Bang(kill_x, kill_y);
13177       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13178         KillPlayer(player);
13179     }
13180     else
13181       Bang(good_x, good_y);
13182   }
13183 }
13184
13185 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13186 {
13187   int i, kill_x = -1, kill_y = -1;
13188   int bad_element = Feld[bad_x][bad_y];
13189   static int test_xy[4][2] =
13190   {
13191     { 0, -1 },
13192     { -1, 0 },
13193     { +1, 0 },
13194     { 0, +1 }
13195   };
13196   static int touch_dir[4] =
13197   {
13198     MV_LEFT | MV_RIGHT,
13199     MV_UP   | MV_DOWN,
13200     MV_UP   | MV_DOWN,
13201     MV_LEFT | MV_RIGHT
13202   };
13203   static int test_dir[4] =
13204   {
13205     MV_UP,
13206     MV_LEFT,
13207     MV_RIGHT,
13208     MV_DOWN
13209   };
13210
13211   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13212     return;
13213
13214   for (i = 0; i < NUM_DIRECTIONS; i++)
13215   {
13216     int test_x, test_y, test_move_dir, test_element;
13217
13218     test_x = bad_x + test_xy[i][0];
13219     test_y = bad_y + test_xy[i][1];
13220
13221     if (!IN_LEV_FIELD(test_x, test_y))
13222       continue;
13223
13224     test_move_dir =
13225       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13226
13227     test_element = Feld[test_x][test_y];
13228
13229     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13230        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13231     */
13232     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13233         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13234     {
13235       // good thing is player or penguin that does not move away
13236       if (IS_PLAYER(test_x, test_y))
13237       {
13238         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13239
13240         if (bad_element == EL_ROBOT && player->is_moving)
13241           continue;     // robot does not kill player if he is moving
13242
13243         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13244         {
13245           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13246             continue;           // center and border element do not touch
13247         }
13248
13249         kill_x = test_x;
13250         kill_y = test_y;
13251
13252         break;
13253       }
13254       else if (test_element == EL_PENGUIN)
13255       {
13256         kill_x = test_x;
13257         kill_y = test_y;
13258
13259         break;
13260       }
13261     }
13262   }
13263
13264   if (kill_x != -1 || kill_y != -1)
13265   {
13266     if (IS_PLAYER(kill_x, kill_y))
13267     {
13268       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13269
13270       if (player->shield_deadly_time_left > 0 &&
13271           !IS_INDESTRUCTIBLE(bad_element))
13272         Bang(bad_x, bad_y);
13273       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13274         KillPlayer(player);
13275     }
13276     else
13277       Bang(kill_x, kill_y);
13278   }
13279 }
13280
13281 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13282 {
13283   int bad_element = Feld[bad_x][bad_y];
13284   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13285   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13286   int test_x = bad_x + dx, test_y = bad_y + dy;
13287   int test_move_dir, test_element;
13288   int kill_x = -1, kill_y = -1;
13289
13290   if (!IN_LEV_FIELD(test_x, test_y))
13291     return;
13292
13293   test_move_dir =
13294     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13295
13296   test_element = Feld[test_x][test_y];
13297
13298   if (test_move_dir != bad_move_dir)
13299   {
13300     // good thing can be player or penguin that does not move away
13301     if (IS_PLAYER(test_x, test_y))
13302     {
13303       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13304
13305       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13306          player as being hit when he is moving towards the bad thing, because
13307          the "get hit by" condition would be lost after the player stops) */
13308       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13309         return;         // player moves away from bad thing
13310
13311       kill_x = test_x;
13312       kill_y = test_y;
13313     }
13314     else if (test_element == EL_PENGUIN)
13315     {
13316       kill_x = test_x;
13317       kill_y = test_y;
13318     }
13319   }
13320
13321   if (kill_x != -1 || kill_y != -1)
13322   {
13323     if (IS_PLAYER(kill_x, kill_y))
13324     {
13325       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13326
13327       if (player->shield_deadly_time_left > 0 &&
13328           !IS_INDESTRUCTIBLE(bad_element))
13329         Bang(bad_x, bad_y);
13330       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13331         KillPlayer(player);
13332     }
13333     else
13334       Bang(kill_x, kill_y);
13335   }
13336 }
13337
13338 void TestIfPlayerTouchesBadThing(int x, int y)
13339 {
13340   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13341 }
13342
13343 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13344 {
13345   TestIfGoodThingHitsBadThing(x, y, move_dir);
13346 }
13347
13348 void TestIfBadThingTouchesPlayer(int x, int y)
13349 {
13350   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13351 }
13352
13353 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13354 {
13355   TestIfBadThingHitsGoodThing(x, y, move_dir);
13356 }
13357
13358 void TestIfFriendTouchesBadThing(int x, int y)
13359 {
13360   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13361 }
13362
13363 void TestIfBadThingTouchesFriend(int x, int y)
13364 {
13365   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13366 }
13367
13368 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13369 {
13370   int i, kill_x = bad_x, kill_y = bad_y;
13371   static int xy[4][2] =
13372   {
13373     { 0, -1 },
13374     { -1, 0 },
13375     { +1, 0 },
13376     { 0, +1 }
13377   };
13378
13379   for (i = 0; i < NUM_DIRECTIONS; i++)
13380   {
13381     int x, y, element;
13382
13383     x = bad_x + xy[i][0];
13384     y = bad_y + xy[i][1];
13385     if (!IN_LEV_FIELD(x, y))
13386       continue;
13387
13388     element = Feld[x][y];
13389     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13390         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13391     {
13392       kill_x = x;
13393       kill_y = y;
13394       break;
13395     }
13396   }
13397
13398   if (kill_x != bad_x || kill_y != bad_y)
13399     Bang(bad_x, bad_y);
13400 }
13401
13402 void KillPlayer(struct PlayerInfo *player)
13403 {
13404   int jx = player->jx, jy = player->jy;
13405
13406   if (!player->active)
13407     return;
13408
13409 #if 0
13410   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13411          player->killed, player->active, player->reanimated);
13412 #endif
13413
13414   /* the following code was introduced to prevent an infinite loop when calling
13415      -> Bang()
13416      -> CheckTriggeredElementChangeExt()
13417      -> ExecuteCustomElementAction()
13418      -> KillPlayer()
13419      -> (infinitely repeating the above sequence of function calls)
13420      which occurs when killing the player while having a CE with the setting
13421      "kill player X when explosion of <player X>"; the solution using a new
13422      field "player->killed" was chosen for backwards compatibility, although
13423      clever use of the fields "player->active" etc. would probably also work */
13424 #if 1
13425   if (player->killed)
13426     return;
13427 #endif
13428
13429   player->killed = TRUE;
13430
13431   // remove accessible field at the player's position
13432   Feld[jx][jy] = EL_EMPTY;
13433
13434   // deactivate shield (else Bang()/Explode() would not work right)
13435   player->shield_normal_time_left = 0;
13436   player->shield_deadly_time_left = 0;
13437
13438 #if 0
13439   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13440          player->killed, player->active, player->reanimated);
13441 #endif
13442
13443   Bang(jx, jy);
13444
13445 #if 0
13446   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13447          player->killed, player->active, player->reanimated);
13448 #endif
13449
13450   if (player->reanimated)       // killed player may have been reanimated
13451     player->killed = player->reanimated = FALSE;
13452   else
13453     BuryPlayer(player);
13454 }
13455
13456 static void KillPlayerUnlessEnemyProtected(int x, int y)
13457 {
13458   if (!PLAYER_ENEMY_PROTECTED(x, y))
13459     KillPlayer(PLAYERINFO(x, y));
13460 }
13461
13462 static void KillPlayerUnlessExplosionProtected(int x, int y)
13463 {
13464   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13465     KillPlayer(PLAYERINFO(x, y));
13466 }
13467
13468 void BuryPlayer(struct PlayerInfo *player)
13469 {
13470   int jx = player->jx, jy = player->jy;
13471
13472   if (!player->active)
13473     return;
13474
13475   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13476   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13477
13478   RemovePlayer(player);
13479
13480   player->buried = TRUE;
13481
13482   if (game.all_players_gone)
13483     game.GameOver = TRUE;
13484 }
13485
13486 void RemovePlayer(struct PlayerInfo *player)
13487 {
13488   int jx = player->jx, jy = player->jy;
13489   int i, found = FALSE;
13490
13491   player->present = FALSE;
13492   player->active = FALSE;
13493
13494   // required for some CE actions (even if the player is not active anymore)
13495   player->MovPos = 0;
13496
13497   if (!ExplodeField[jx][jy])
13498     StorePlayer[jx][jy] = 0;
13499
13500   if (player->is_moving)
13501     TEST_DrawLevelField(player->last_jx, player->last_jy);
13502
13503   for (i = 0; i < MAX_PLAYERS; i++)
13504     if (stored_player[i].active)
13505       found = TRUE;
13506
13507   if (!found)
13508   {
13509     game.all_players_gone = TRUE;
13510     game.GameOver = TRUE;
13511   }
13512
13513   game.exit_x = game.robot_wheel_x = jx;
13514   game.exit_y = game.robot_wheel_y = jy;
13515 }
13516
13517 void ExitPlayer(struct PlayerInfo *player)
13518 {
13519   DrawPlayer(player);   // needed here only to cleanup last field
13520   RemovePlayer(player);
13521
13522   if (game.players_still_needed > 0)
13523     game.players_still_needed--;
13524 }
13525
13526 static void setFieldForSnapping(int x, int y, int element, int direction)
13527 {
13528   struct ElementInfo *ei = &element_info[element];
13529   int direction_bit = MV_DIR_TO_BIT(direction);
13530   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13531   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13532                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13533
13534   Feld[x][y] = EL_ELEMENT_SNAPPING;
13535   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13536
13537   ResetGfxAnimation(x, y);
13538
13539   GfxElement[x][y] = element;
13540   GfxAction[x][y] = action;
13541   GfxDir[x][y] = direction;
13542   GfxFrame[x][y] = -1;
13543 }
13544
13545 /*
13546   =============================================================================
13547   checkDiagonalPushing()
13548   -----------------------------------------------------------------------------
13549   check if diagonal input device direction results in pushing of object
13550   (by checking if the alternative direction is walkable, diggable, ...)
13551   =============================================================================
13552 */
13553
13554 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13555                                     int x, int y, int real_dx, int real_dy)
13556 {
13557   int jx, jy, dx, dy, xx, yy;
13558
13559   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13560     return TRUE;
13561
13562   // diagonal direction: check alternative direction
13563   jx = player->jx;
13564   jy = player->jy;
13565   dx = x - jx;
13566   dy = y - jy;
13567   xx = jx + (dx == 0 ? real_dx : 0);
13568   yy = jy + (dy == 0 ? real_dy : 0);
13569
13570   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13571 }
13572
13573 /*
13574   =============================================================================
13575   DigField()
13576   -----------------------------------------------------------------------------
13577   x, y:                 field next to player (non-diagonal) to try to dig to
13578   real_dx, real_dy:     direction as read from input device (can be diagonal)
13579   =============================================================================
13580 */
13581
13582 static int DigField(struct PlayerInfo *player,
13583                     int oldx, int oldy, int x, int y,
13584                     int real_dx, int real_dy, int mode)
13585 {
13586   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13587   boolean player_was_pushing = player->is_pushing;
13588   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13589   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13590   int jx = oldx, jy = oldy;
13591   int dx = x - jx, dy = y - jy;
13592   int nextx = x + dx, nexty = y + dy;
13593   int move_direction = (dx == -1 ? MV_LEFT  :
13594                         dx == +1 ? MV_RIGHT :
13595                         dy == -1 ? MV_UP    :
13596                         dy == +1 ? MV_DOWN  : MV_NONE);
13597   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13598   int dig_side = MV_DIR_OPPOSITE(move_direction);
13599   int old_element = Feld[jx][jy];
13600   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13601   int collect_count;
13602
13603   if (is_player)                // function can also be called by EL_PENGUIN
13604   {
13605     if (player->MovPos == 0)
13606     {
13607       player->is_digging = FALSE;
13608       player->is_collecting = FALSE;
13609     }
13610
13611     if (player->MovPos == 0)    // last pushing move finished
13612       player->is_pushing = FALSE;
13613
13614     if (mode == DF_NO_PUSH)     // player just stopped pushing
13615     {
13616       player->is_switching = FALSE;
13617       player->push_delay = -1;
13618
13619       return MP_NO_ACTION;
13620     }
13621   }
13622
13623   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13624     old_element = Back[jx][jy];
13625
13626   // in case of element dropped at player position, check background
13627   else if (Back[jx][jy] != EL_EMPTY &&
13628            game.engine_version >= VERSION_IDENT(2,2,0,0))
13629     old_element = Back[jx][jy];
13630
13631   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13632     return MP_NO_ACTION;        // field has no opening in this direction
13633
13634   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13635     return MP_NO_ACTION;        // field has no opening in this direction
13636
13637   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13638   {
13639     SplashAcid(x, y);
13640
13641     Feld[jx][jy] = player->artwork_element;
13642     InitMovingField(jx, jy, MV_DOWN);
13643     Store[jx][jy] = EL_ACID;
13644     ContinueMoving(jx, jy);
13645     BuryPlayer(player);
13646
13647     return MP_DONT_RUN_INTO;
13648   }
13649
13650   if (player_can_move && DONT_RUN_INTO(element))
13651   {
13652     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13653
13654     return MP_DONT_RUN_INTO;
13655   }
13656
13657   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13658     return MP_NO_ACTION;
13659
13660   collect_count = element_info[element].collect_count_initial;
13661
13662   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13663     return MP_NO_ACTION;
13664
13665   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13666     player_can_move = player_can_move_or_snap;
13667
13668   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13669       game.engine_version >= VERSION_IDENT(2,2,0,0))
13670   {
13671     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13672                                player->index_bit, dig_side);
13673     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13674                                         player->index_bit, dig_side);
13675
13676     if (element == EL_DC_LANDMINE)
13677       Bang(x, y);
13678
13679     if (Feld[x][y] != element)          // field changed by snapping
13680       return MP_ACTION;
13681
13682     return MP_NO_ACTION;
13683   }
13684
13685   if (player->gravity && is_player && !player->is_auto_moving &&
13686       canFallDown(player) && move_direction != MV_DOWN &&
13687       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13688     return MP_NO_ACTION;        // player cannot walk here due to gravity
13689
13690   if (player_can_move &&
13691       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13692   {
13693     int sound_element = SND_ELEMENT(element);
13694     int sound_action = ACTION_WALKING;
13695
13696     if (IS_RND_GATE(element))
13697     {
13698       if (!player->key[RND_GATE_NR(element)])
13699         return MP_NO_ACTION;
13700     }
13701     else if (IS_RND_GATE_GRAY(element))
13702     {
13703       if (!player->key[RND_GATE_GRAY_NR(element)])
13704         return MP_NO_ACTION;
13705     }
13706     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13707     {
13708       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13709         return MP_NO_ACTION;
13710     }
13711     else if (element == EL_EXIT_OPEN ||
13712              element == EL_EM_EXIT_OPEN ||
13713              element == EL_EM_EXIT_OPENING ||
13714              element == EL_STEEL_EXIT_OPEN ||
13715              element == EL_EM_STEEL_EXIT_OPEN ||
13716              element == EL_EM_STEEL_EXIT_OPENING ||
13717              element == EL_SP_EXIT_OPEN ||
13718              element == EL_SP_EXIT_OPENING)
13719     {
13720       sound_action = ACTION_PASSING;    // player is passing exit
13721     }
13722     else if (element == EL_EMPTY)
13723     {
13724       sound_action = ACTION_MOVING;             // nothing to walk on
13725     }
13726
13727     // play sound from background or player, whatever is available
13728     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13729       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13730     else
13731       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13732   }
13733   else if (player_can_move &&
13734            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13735   {
13736     if (!ACCESS_FROM(element, opposite_direction))
13737       return MP_NO_ACTION;      // field not accessible from this direction
13738
13739     if (CAN_MOVE(element))      // only fixed elements can be passed!
13740       return MP_NO_ACTION;
13741
13742     if (IS_EM_GATE(element))
13743     {
13744       if (!player->key[EM_GATE_NR(element)])
13745         return MP_NO_ACTION;
13746     }
13747     else if (IS_EM_GATE_GRAY(element))
13748     {
13749       if (!player->key[EM_GATE_GRAY_NR(element)])
13750         return MP_NO_ACTION;
13751     }
13752     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13753     {
13754       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13755         return MP_NO_ACTION;
13756     }
13757     else if (IS_EMC_GATE(element))
13758     {
13759       if (!player->key[EMC_GATE_NR(element)])
13760         return MP_NO_ACTION;
13761     }
13762     else if (IS_EMC_GATE_GRAY(element))
13763     {
13764       if (!player->key[EMC_GATE_GRAY_NR(element)])
13765         return MP_NO_ACTION;
13766     }
13767     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13768     {
13769       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13770         return MP_NO_ACTION;
13771     }
13772     else if (element == EL_DC_GATE_WHITE ||
13773              element == EL_DC_GATE_WHITE_GRAY ||
13774              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13775     {
13776       if (player->num_white_keys == 0)
13777         return MP_NO_ACTION;
13778
13779       player->num_white_keys--;
13780     }
13781     else if (IS_SP_PORT(element))
13782     {
13783       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13784           element == EL_SP_GRAVITY_PORT_RIGHT ||
13785           element == EL_SP_GRAVITY_PORT_UP ||
13786           element == EL_SP_GRAVITY_PORT_DOWN)
13787         player->gravity = !player->gravity;
13788       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13789                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13790                element == EL_SP_GRAVITY_ON_PORT_UP ||
13791                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13792         player->gravity = TRUE;
13793       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13794                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13795                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13796                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13797         player->gravity = FALSE;
13798     }
13799
13800     // automatically move to the next field with double speed
13801     player->programmed_action = move_direction;
13802
13803     if (player->move_delay_reset_counter == 0)
13804     {
13805       player->move_delay_reset_counter = 2;     // two double speed steps
13806
13807       DOUBLE_PLAYER_SPEED(player);
13808     }
13809
13810     PlayLevelSoundAction(x, y, ACTION_PASSING);
13811   }
13812   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13813   {
13814     RemoveField(x, y);
13815
13816     if (mode != DF_SNAP)
13817     {
13818       GfxElement[x][y] = GFX_ELEMENT(element);
13819       player->is_digging = TRUE;
13820     }
13821
13822     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13823
13824     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13825                                         player->index_bit, dig_side);
13826
13827     if (mode == DF_SNAP)
13828     {
13829       if (level.block_snap_field)
13830         setFieldForSnapping(x, y, element, move_direction);
13831       else
13832         TestIfElementTouchesCustomElement(x, y);        // for empty space
13833
13834       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13835                                           player->index_bit, dig_side);
13836     }
13837   }
13838   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13839   {
13840     RemoveField(x, y);
13841
13842     if (is_player && mode != DF_SNAP)
13843     {
13844       GfxElement[x][y] = element;
13845       player->is_collecting = TRUE;
13846     }
13847
13848     if (element == EL_SPEED_PILL)
13849     {
13850       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13851     }
13852     else if (element == EL_EXTRA_TIME && level.time > 0)
13853     {
13854       TimeLeft += level.extra_time;
13855
13856       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13857
13858       DisplayGameControlValues();
13859     }
13860     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13861     {
13862       player->shield_normal_time_left += level.shield_normal_time;
13863       if (element == EL_SHIELD_DEADLY)
13864         player->shield_deadly_time_left += level.shield_deadly_time;
13865     }
13866     else if (element == EL_DYNAMITE ||
13867              element == EL_EM_DYNAMITE ||
13868              element == EL_SP_DISK_RED)
13869     {
13870       if (player->inventory_size < MAX_INVENTORY_SIZE)
13871         player->inventory_element[player->inventory_size++] = element;
13872
13873       DrawGameDoorValues();
13874     }
13875     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13876     {
13877       player->dynabomb_count++;
13878       player->dynabombs_left++;
13879     }
13880     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13881     {
13882       player->dynabomb_size++;
13883     }
13884     else if (element == EL_DYNABOMB_INCREASE_POWER)
13885     {
13886       player->dynabomb_xl = TRUE;
13887     }
13888     else if (IS_KEY(element))
13889     {
13890       player->key[KEY_NR(element)] = TRUE;
13891
13892       DrawGameDoorValues();
13893     }
13894     else if (element == EL_DC_KEY_WHITE)
13895     {
13896       player->num_white_keys++;
13897
13898       // display white keys?
13899       // DrawGameDoorValues();
13900     }
13901     else if (IS_ENVELOPE(element))
13902     {
13903       player->show_envelope = element;
13904     }
13905     else if (element == EL_EMC_LENSES)
13906     {
13907       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13908
13909       RedrawAllInvisibleElementsForLenses();
13910     }
13911     else if (element == EL_EMC_MAGNIFIER)
13912     {
13913       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13914
13915       RedrawAllInvisibleElementsForMagnifier();
13916     }
13917     else if (IS_DROPPABLE(element) ||
13918              IS_THROWABLE(element))     // can be collected and dropped
13919     {
13920       int i;
13921
13922       if (collect_count == 0)
13923         player->inventory_infinite_element = element;
13924       else
13925         for (i = 0; i < collect_count; i++)
13926           if (player->inventory_size < MAX_INVENTORY_SIZE)
13927             player->inventory_element[player->inventory_size++] = element;
13928
13929       DrawGameDoorValues();
13930     }
13931     else if (collect_count > 0)
13932     {
13933       game.gems_still_needed -= collect_count;
13934       if (game.gems_still_needed < 0)
13935         game.gems_still_needed = 0;
13936
13937       game.snapshot.collected_item = TRUE;
13938
13939       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13940
13941       DisplayGameControlValues();
13942     }
13943
13944     RaiseScoreElement(element);
13945     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13946
13947     if (is_player)
13948       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13949                                           player->index_bit, dig_side);
13950
13951     if (mode == DF_SNAP)
13952     {
13953       if (level.block_snap_field)
13954         setFieldForSnapping(x, y, element, move_direction);
13955       else
13956         TestIfElementTouchesCustomElement(x, y);        // for empty space
13957
13958       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13959                                           player->index_bit, dig_side);
13960     }
13961   }
13962   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13963   {
13964     if (mode == DF_SNAP && element != EL_BD_ROCK)
13965       return MP_NO_ACTION;
13966
13967     if (CAN_FALL(element) && dy)
13968       return MP_NO_ACTION;
13969
13970     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13971         !(element == EL_SPRING && level.use_spring_bug))
13972       return MP_NO_ACTION;
13973
13974     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13975         ((move_direction & MV_VERTICAL &&
13976           ((element_info[element].move_pattern & MV_LEFT &&
13977             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13978            (element_info[element].move_pattern & MV_RIGHT &&
13979             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13980          (move_direction & MV_HORIZONTAL &&
13981           ((element_info[element].move_pattern & MV_UP &&
13982             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13983            (element_info[element].move_pattern & MV_DOWN &&
13984             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13985       return MP_NO_ACTION;
13986
13987     // do not push elements already moving away faster than player
13988     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13989         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13990       return MP_NO_ACTION;
13991
13992     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13993     {
13994       if (player->push_delay_value == -1 || !player_was_pushing)
13995         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13996     }
13997     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13998     {
13999       if (player->push_delay_value == -1)
14000         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14001     }
14002     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14003     {
14004       if (!player->is_pushing)
14005         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14006     }
14007
14008     player->is_pushing = TRUE;
14009     player->is_active = TRUE;
14010
14011     if (!(IN_LEV_FIELD(nextx, nexty) &&
14012           (IS_FREE(nextx, nexty) ||
14013            (IS_SB_ELEMENT(element) &&
14014             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14015            (IS_CUSTOM_ELEMENT(element) &&
14016             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14017       return MP_NO_ACTION;
14018
14019     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14020       return MP_NO_ACTION;
14021
14022     if (player->push_delay == -1)       // new pushing; restart delay
14023       player->push_delay = 0;
14024
14025     if (player->push_delay < player->push_delay_value &&
14026         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14027         element != EL_SPRING && element != EL_BALLOON)
14028     {
14029       // make sure that there is no move delay before next try to push
14030       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14031         player->move_delay = 0;
14032
14033       return MP_NO_ACTION;
14034     }
14035
14036     if (IS_CUSTOM_ELEMENT(element) &&
14037         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14038     {
14039       if (!DigFieldByCE(nextx, nexty, element))
14040         return MP_NO_ACTION;
14041     }
14042
14043     if (IS_SB_ELEMENT(element))
14044     {
14045       boolean sokoban_task_solved = FALSE;
14046
14047       if (element == EL_SOKOBAN_FIELD_FULL)
14048       {
14049         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14050
14051         IncrementSokobanFieldsNeeded();
14052         IncrementSokobanObjectsNeeded();
14053       }
14054
14055       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14056       {
14057         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14058
14059         DecrementSokobanFieldsNeeded();
14060         DecrementSokobanObjectsNeeded();
14061
14062         // sokoban object was pushed from empty field to sokoban field
14063         if (Back[x][y] == EL_EMPTY)
14064           sokoban_task_solved = TRUE;
14065       }
14066
14067       Feld[x][y] = EL_SOKOBAN_OBJECT;
14068
14069       if (Back[x][y] == Back[nextx][nexty])
14070         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14071       else if (Back[x][y] != 0)
14072         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14073                                     ACTION_EMPTYING);
14074       else
14075         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14076                                     ACTION_FILLING);
14077
14078       if (sokoban_task_solved &&
14079           game.sokoban_fields_still_needed == 0 &&
14080           game.sokoban_objects_still_needed == 0 &&
14081           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14082       {
14083         game.players_still_needed = 0;
14084
14085         LevelSolved();
14086
14087         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14088       }
14089     }
14090     else
14091       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14092
14093     InitMovingField(x, y, move_direction);
14094     GfxAction[x][y] = ACTION_PUSHING;
14095
14096     if (mode == DF_SNAP)
14097       ContinueMoving(x, y);
14098     else
14099       MovPos[x][y] = (dx != 0 ? dx : dy);
14100
14101     Pushed[x][y] = TRUE;
14102     Pushed[nextx][nexty] = TRUE;
14103
14104     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14105       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14106     else
14107       player->push_delay_value = -1;    // get new value later
14108
14109     // check for element change _after_ element has been pushed
14110     if (game.use_change_when_pushing_bug)
14111     {
14112       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14113                                  player->index_bit, dig_side);
14114       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14115                                           player->index_bit, dig_side);
14116     }
14117   }
14118   else if (IS_SWITCHABLE(element))
14119   {
14120     if (PLAYER_SWITCHING(player, x, y))
14121     {
14122       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14123                                           player->index_bit, dig_side);
14124
14125       return MP_ACTION;
14126     }
14127
14128     player->is_switching = TRUE;
14129     player->switch_x = x;
14130     player->switch_y = y;
14131
14132     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14133
14134     if (element == EL_ROBOT_WHEEL)
14135     {
14136       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14137
14138       game.robot_wheel_x = x;
14139       game.robot_wheel_y = y;
14140       game.robot_wheel_active = TRUE;
14141
14142       TEST_DrawLevelField(x, y);
14143     }
14144     else if (element == EL_SP_TERMINAL)
14145     {
14146       int xx, yy;
14147
14148       SCAN_PLAYFIELD(xx, yy)
14149       {
14150         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14151         {
14152           Bang(xx, yy);
14153         }
14154         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14155         {
14156           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14157
14158           ResetGfxAnimation(xx, yy);
14159           TEST_DrawLevelField(xx, yy);
14160         }
14161       }
14162     }
14163     else if (IS_BELT_SWITCH(element))
14164     {
14165       ToggleBeltSwitch(x, y);
14166     }
14167     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14168              element == EL_SWITCHGATE_SWITCH_DOWN ||
14169              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14170              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14171     {
14172       ToggleSwitchgateSwitch(x, y);
14173     }
14174     else if (element == EL_LIGHT_SWITCH ||
14175              element == EL_LIGHT_SWITCH_ACTIVE)
14176     {
14177       ToggleLightSwitch(x, y);
14178     }
14179     else if (element == EL_TIMEGATE_SWITCH ||
14180              element == EL_DC_TIMEGATE_SWITCH)
14181     {
14182       ActivateTimegateSwitch(x, y);
14183     }
14184     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14185              element == EL_BALLOON_SWITCH_RIGHT ||
14186              element == EL_BALLOON_SWITCH_UP    ||
14187              element == EL_BALLOON_SWITCH_DOWN  ||
14188              element == EL_BALLOON_SWITCH_NONE  ||
14189              element == EL_BALLOON_SWITCH_ANY)
14190     {
14191       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14192                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14193                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14194                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14195                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14196                              move_direction);
14197     }
14198     else if (element == EL_LAMP)
14199     {
14200       Feld[x][y] = EL_LAMP_ACTIVE;
14201       game.lights_still_needed--;
14202
14203       ResetGfxAnimation(x, y);
14204       TEST_DrawLevelField(x, y);
14205     }
14206     else if (element == EL_TIME_ORB_FULL)
14207     {
14208       Feld[x][y] = EL_TIME_ORB_EMPTY;
14209
14210       if (level.time > 0 || level.use_time_orb_bug)
14211       {
14212         TimeLeft += level.time_orb_time;
14213         game.no_time_limit = FALSE;
14214
14215         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14216
14217         DisplayGameControlValues();
14218       }
14219
14220       ResetGfxAnimation(x, y);
14221       TEST_DrawLevelField(x, y);
14222     }
14223     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14224              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14225     {
14226       int xx, yy;
14227
14228       game.ball_state = !game.ball_state;
14229
14230       SCAN_PLAYFIELD(xx, yy)
14231       {
14232         int e = Feld[xx][yy];
14233
14234         if (game.ball_state)
14235         {
14236           if (e == EL_EMC_MAGIC_BALL)
14237             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14238           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14239             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14240         }
14241         else
14242         {
14243           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14244             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14245           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14246             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14247         }
14248       }
14249     }
14250
14251     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14252                                         player->index_bit, dig_side);
14253
14254     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14255                                         player->index_bit, dig_side);
14256
14257     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14258                                         player->index_bit, dig_side);
14259
14260     return MP_ACTION;
14261   }
14262   else
14263   {
14264     if (!PLAYER_SWITCHING(player, x, y))
14265     {
14266       player->is_switching = TRUE;
14267       player->switch_x = x;
14268       player->switch_y = y;
14269
14270       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14271                                  player->index_bit, dig_side);
14272       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14273                                           player->index_bit, dig_side);
14274
14275       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14276                                  player->index_bit, dig_side);
14277       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14278                                           player->index_bit, dig_side);
14279     }
14280
14281     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14282                                player->index_bit, dig_side);
14283     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14284                                         player->index_bit, dig_side);
14285
14286     return MP_NO_ACTION;
14287   }
14288
14289   player->push_delay = -1;
14290
14291   if (is_player)                // function can also be called by EL_PENGUIN
14292   {
14293     if (Feld[x][y] != element)          // really digged/collected something
14294     {
14295       player->is_collecting = !player->is_digging;
14296       player->is_active = TRUE;
14297     }
14298   }
14299
14300   return MP_MOVING;
14301 }
14302
14303 static boolean DigFieldByCE(int x, int y, int digging_element)
14304 {
14305   int element = Feld[x][y];
14306
14307   if (!IS_FREE(x, y))
14308   {
14309     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14310                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14311                   ACTION_BREAKING);
14312
14313     // no element can dig solid indestructible elements
14314     if (IS_INDESTRUCTIBLE(element) &&
14315         !IS_DIGGABLE(element) &&
14316         !IS_COLLECTIBLE(element))
14317       return FALSE;
14318
14319     if (AmoebaNr[x][y] &&
14320         (element == EL_AMOEBA_FULL ||
14321          element == EL_BD_AMOEBA ||
14322          element == EL_AMOEBA_GROWING))
14323     {
14324       AmoebaCnt[AmoebaNr[x][y]]--;
14325       AmoebaCnt2[AmoebaNr[x][y]]--;
14326     }
14327
14328     if (IS_MOVING(x, y))
14329       RemoveMovingField(x, y);
14330     else
14331     {
14332       RemoveField(x, y);
14333       TEST_DrawLevelField(x, y);
14334     }
14335
14336     // if digged element was about to explode, prevent the explosion
14337     ExplodeField[x][y] = EX_TYPE_NONE;
14338
14339     PlayLevelSoundAction(x, y, action);
14340   }
14341
14342   Store[x][y] = EL_EMPTY;
14343
14344   // this makes it possible to leave the removed element again
14345   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14346     Store[x][y] = element;
14347
14348   return TRUE;
14349 }
14350
14351 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14352 {
14353   int jx = player->jx, jy = player->jy;
14354   int x = jx + dx, y = jy + dy;
14355   int snap_direction = (dx == -1 ? MV_LEFT  :
14356                         dx == +1 ? MV_RIGHT :
14357                         dy == -1 ? MV_UP    :
14358                         dy == +1 ? MV_DOWN  : MV_NONE);
14359   boolean can_continue_snapping = (level.continuous_snapping &&
14360                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14361
14362   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14363     return FALSE;
14364
14365   if (!player->active || !IN_LEV_FIELD(x, y))
14366     return FALSE;
14367
14368   if (dx && dy)
14369     return FALSE;
14370
14371   if (!dx && !dy)
14372   {
14373     if (player->MovPos == 0)
14374       player->is_pushing = FALSE;
14375
14376     player->is_snapping = FALSE;
14377
14378     if (player->MovPos == 0)
14379     {
14380       player->is_moving = FALSE;
14381       player->is_digging = FALSE;
14382       player->is_collecting = FALSE;
14383     }
14384
14385     return FALSE;
14386   }
14387
14388   // prevent snapping with already pressed snap key when not allowed
14389   if (player->is_snapping && !can_continue_snapping)
14390     return FALSE;
14391
14392   player->MovDir = snap_direction;
14393
14394   if (player->MovPos == 0)
14395   {
14396     player->is_moving = FALSE;
14397     player->is_digging = FALSE;
14398     player->is_collecting = FALSE;
14399   }
14400
14401   player->is_dropping = FALSE;
14402   player->is_dropping_pressed = FALSE;
14403   player->drop_pressed_delay = 0;
14404
14405   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14406     return FALSE;
14407
14408   player->is_snapping = TRUE;
14409   player->is_active = TRUE;
14410
14411   if (player->MovPos == 0)
14412   {
14413     player->is_moving = FALSE;
14414     player->is_digging = FALSE;
14415     player->is_collecting = FALSE;
14416   }
14417
14418   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14419     TEST_DrawLevelField(player->last_jx, player->last_jy);
14420
14421   TEST_DrawLevelField(x, y);
14422
14423   return TRUE;
14424 }
14425
14426 static boolean DropElement(struct PlayerInfo *player)
14427 {
14428   int old_element, new_element;
14429   int dropx = player->jx, dropy = player->jy;
14430   int drop_direction = player->MovDir;
14431   int drop_side = drop_direction;
14432   int drop_element = get_next_dropped_element(player);
14433
14434   /* do not drop an element on top of another element; when holding drop key
14435      pressed without moving, dropped element must move away before the next
14436      element can be dropped (this is especially important if the next element
14437      is dynamite, which can be placed on background for historical reasons) */
14438   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14439     return MP_ACTION;
14440
14441   if (IS_THROWABLE(drop_element))
14442   {
14443     dropx += GET_DX_FROM_DIR(drop_direction);
14444     dropy += GET_DY_FROM_DIR(drop_direction);
14445
14446     if (!IN_LEV_FIELD(dropx, dropy))
14447       return FALSE;
14448   }
14449
14450   old_element = Feld[dropx][dropy];     // old element at dropping position
14451   new_element = drop_element;           // default: no change when dropping
14452
14453   // check if player is active, not moving and ready to drop
14454   if (!player->active || player->MovPos || player->drop_delay > 0)
14455     return FALSE;
14456
14457   // check if player has anything that can be dropped
14458   if (new_element == EL_UNDEFINED)
14459     return FALSE;
14460
14461   // only set if player has anything that can be dropped
14462   player->is_dropping_pressed = TRUE;
14463
14464   // check if drop key was pressed long enough for EM style dynamite
14465   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14466     return FALSE;
14467
14468   // check if anything can be dropped at the current position
14469   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14470     return FALSE;
14471
14472   // collected custom elements can only be dropped on empty fields
14473   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14474     return FALSE;
14475
14476   if (old_element != EL_EMPTY)
14477     Back[dropx][dropy] = old_element;   // store old element on this field
14478
14479   ResetGfxAnimation(dropx, dropy);
14480   ResetRandomAnimationValue(dropx, dropy);
14481
14482   if (player->inventory_size > 0 ||
14483       player->inventory_infinite_element != EL_UNDEFINED)
14484   {
14485     if (player->inventory_size > 0)
14486     {
14487       player->inventory_size--;
14488
14489       DrawGameDoorValues();
14490
14491       if (new_element == EL_DYNAMITE)
14492         new_element = EL_DYNAMITE_ACTIVE;
14493       else if (new_element == EL_EM_DYNAMITE)
14494         new_element = EL_EM_DYNAMITE_ACTIVE;
14495       else if (new_element == EL_SP_DISK_RED)
14496         new_element = EL_SP_DISK_RED_ACTIVE;
14497     }
14498
14499     Feld[dropx][dropy] = new_element;
14500
14501     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14502       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14503                           el2img(Feld[dropx][dropy]), 0);
14504
14505     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14506
14507     // needed if previous element just changed to "empty" in the last frame
14508     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14509
14510     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14511                                player->index_bit, drop_side);
14512     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14513                                         CE_PLAYER_DROPS_X,
14514                                         player->index_bit, drop_side);
14515
14516     TestIfElementTouchesCustomElement(dropx, dropy);
14517   }
14518   else          // player is dropping a dyna bomb
14519   {
14520     player->dynabombs_left--;
14521
14522     Feld[dropx][dropy] = new_element;
14523
14524     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14525       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14526                           el2img(Feld[dropx][dropy]), 0);
14527
14528     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14529   }
14530
14531   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14532     InitField_WithBug1(dropx, dropy, FALSE);
14533
14534   new_element = Feld[dropx][dropy];     // element might have changed
14535
14536   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14537       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14538   {
14539     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14540       MovDir[dropx][dropy] = drop_direction;
14541
14542     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14543
14544     // do not cause impact style collision by dropping elements that can fall
14545     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14546   }
14547
14548   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14549   player->is_dropping = TRUE;
14550
14551   player->drop_pressed_delay = 0;
14552   player->is_dropping_pressed = FALSE;
14553
14554   player->drop_x = dropx;
14555   player->drop_y = dropy;
14556
14557   return TRUE;
14558 }
14559
14560 // ----------------------------------------------------------------------------
14561 // game sound playing functions
14562 // ----------------------------------------------------------------------------
14563
14564 static int *loop_sound_frame = NULL;
14565 static int *loop_sound_volume = NULL;
14566
14567 void InitPlayLevelSound(void)
14568 {
14569   int num_sounds = getSoundListSize();
14570
14571   checked_free(loop_sound_frame);
14572   checked_free(loop_sound_volume);
14573
14574   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14575   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14576 }
14577
14578 static void PlayLevelSound(int x, int y, int nr)
14579 {
14580   int sx = SCREENX(x), sy = SCREENY(y);
14581   int volume, stereo_position;
14582   int max_distance = 8;
14583   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14584
14585   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14586       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14587     return;
14588
14589   if (!IN_LEV_FIELD(x, y) ||
14590       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14591       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14592     return;
14593
14594   volume = SOUND_MAX_VOLUME;
14595
14596   if (!IN_SCR_FIELD(sx, sy))
14597   {
14598     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14599     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14600
14601     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14602   }
14603
14604   stereo_position = (SOUND_MAX_LEFT +
14605                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14606                      (SCR_FIELDX + 2 * max_distance));
14607
14608   if (IS_LOOP_SOUND(nr))
14609   {
14610     /* This assures that quieter loop sounds do not overwrite louder ones,
14611        while restarting sound volume comparison with each new game frame. */
14612
14613     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14614       return;
14615
14616     loop_sound_volume[nr] = volume;
14617     loop_sound_frame[nr] = FrameCounter;
14618   }
14619
14620   PlaySoundExt(nr, volume, stereo_position, type);
14621 }
14622
14623 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14624 {
14625   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14626                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14627                  y < LEVELY(BY1) ? LEVELY(BY1) :
14628                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14629                  sound_action);
14630 }
14631
14632 static void PlayLevelSoundAction(int x, int y, int action)
14633 {
14634   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14635 }
14636
14637 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14638 {
14639   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14640
14641   if (sound_effect != SND_UNDEFINED)
14642     PlayLevelSound(x, y, sound_effect);
14643 }
14644
14645 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14646                                               int action)
14647 {
14648   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14649
14650   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14651     PlayLevelSound(x, y, sound_effect);
14652 }
14653
14654 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14655 {
14656   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14657
14658   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14659     PlayLevelSound(x, y, sound_effect);
14660 }
14661
14662 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14663 {
14664   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14665
14666   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14667     StopSound(sound_effect);
14668 }
14669
14670 static int getLevelMusicNr(void)
14671 {
14672   if (levelset.music[level_nr] != MUS_UNDEFINED)
14673     return levelset.music[level_nr];            // from config file
14674   else
14675     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14676 }
14677
14678 static void FadeLevelSounds(void)
14679 {
14680   FadeSounds();
14681 }
14682
14683 static void FadeLevelMusic(void)
14684 {
14685   int music_nr = getLevelMusicNr();
14686   char *curr_music = getCurrentlyPlayingMusicFilename();
14687   char *next_music = getMusicInfoEntryFilename(music_nr);
14688
14689   if (!strEqual(curr_music, next_music))
14690     FadeMusic();
14691 }
14692
14693 void FadeLevelSoundsAndMusic(void)
14694 {
14695   FadeLevelSounds();
14696   FadeLevelMusic();
14697 }
14698
14699 static void PlayLevelMusic(void)
14700 {
14701   int music_nr = getLevelMusicNr();
14702   char *curr_music = getCurrentlyPlayingMusicFilename();
14703   char *next_music = getMusicInfoEntryFilename(music_nr);
14704
14705   if (!strEqual(curr_music, next_music))
14706     PlayMusicLoop(music_nr);
14707 }
14708
14709 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14710 {
14711   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14712   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14713   int x = xx - 1 - offset;
14714   int y = yy - 1 - offset;
14715
14716   switch (sample)
14717   {
14718     case SOUND_blank:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14720       break;
14721
14722     case SOUND_roll:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14724       break;
14725
14726     case SOUND_stone:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14728       break;
14729
14730     case SOUND_nut:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14732       break;
14733
14734     case SOUND_crack:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14736       break;
14737
14738     case SOUND_bug:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14740       break;
14741
14742     case SOUND_tank:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14744       break;
14745
14746     case SOUND_android_clone:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14748       break;
14749
14750     case SOUND_android_move:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14752       break;
14753
14754     case SOUND_spring:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14756       break;
14757
14758     case SOUND_slurp:
14759       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14760       break;
14761
14762     case SOUND_eater:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14764       break;
14765
14766     case SOUND_eater_eat:
14767       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14768       break;
14769
14770     case SOUND_alien:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14772       break;
14773
14774     case SOUND_collect:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14776       break;
14777
14778     case SOUND_diamond:
14779       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14780       break;
14781
14782     case SOUND_squash:
14783       // !!! CHECK THIS !!!
14784 #if 1
14785       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14786 #else
14787       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14788 #endif
14789       break;
14790
14791     case SOUND_wonderfall:
14792       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14793       break;
14794
14795     case SOUND_drip:
14796       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14797       break;
14798
14799     case SOUND_push:
14800       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14801       break;
14802
14803     case SOUND_dirt:
14804       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14805       break;
14806
14807     case SOUND_acid:
14808       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14809       break;
14810
14811     case SOUND_ball:
14812       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14813       break;
14814
14815     case SOUND_slide:
14816       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14817       break;
14818
14819     case SOUND_wonder:
14820       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14821       break;
14822
14823     case SOUND_door:
14824       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14825       break;
14826
14827     case SOUND_exit_open:
14828       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14829       break;
14830
14831     case SOUND_exit_leave:
14832       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14833       break;
14834
14835     case SOUND_dynamite:
14836       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14837       break;
14838
14839     case SOUND_tick:
14840       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14841       break;
14842
14843     case SOUND_press:
14844       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14845       break;
14846
14847     case SOUND_wheel:
14848       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14849       break;
14850
14851     case SOUND_boom:
14852       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14853       break;
14854
14855     case SOUND_die:
14856       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14857       break;
14858
14859     case SOUND_time:
14860       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14861       break;
14862
14863     default:
14864       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14865       break;
14866   }
14867 }
14868
14869 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14870 {
14871   int element = map_element_SP_to_RND(element_sp);
14872   int action = map_action_SP_to_RND(action_sp);
14873   int offset = (setup.sp_show_border_elements ? 0 : 1);
14874   int x = xx - offset;
14875   int y = yy - offset;
14876
14877   PlayLevelSoundElementAction(x, y, element, action);
14878 }
14879
14880 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14881 {
14882   int element = map_element_MM_to_RND(element_mm);
14883   int action = map_action_MM_to_RND(action_mm);
14884   int offset = 0;
14885   int x = xx - offset;
14886   int y = yy - offset;
14887
14888   if (!IS_MM_ELEMENT(element))
14889     element = EL_MM_DEFAULT;
14890
14891   PlayLevelSoundElementAction(x, y, element, action);
14892 }
14893
14894 void PlaySound_MM(int sound_mm)
14895 {
14896   int sound = map_sound_MM_to_RND(sound_mm);
14897
14898   if (sound == SND_UNDEFINED)
14899     return;
14900
14901   PlaySound(sound);
14902 }
14903
14904 void PlaySoundLoop_MM(int sound_mm)
14905 {
14906   int sound = map_sound_MM_to_RND(sound_mm);
14907
14908   if (sound == SND_UNDEFINED)
14909     return;
14910
14911   PlaySoundLoop(sound);
14912 }
14913
14914 void StopSound_MM(int sound_mm)
14915 {
14916   int sound = map_sound_MM_to_RND(sound_mm);
14917
14918   if (sound == SND_UNDEFINED)
14919     return;
14920
14921   StopSound(sound);
14922 }
14923
14924 void RaiseScore(int value)
14925 {
14926   game.score += value;
14927
14928   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14929
14930   DisplayGameControlValues();
14931 }
14932
14933 void RaiseScoreElement(int element)
14934 {
14935   switch (element)
14936   {
14937     case EL_EMERALD:
14938     case EL_BD_DIAMOND:
14939     case EL_EMERALD_YELLOW:
14940     case EL_EMERALD_RED:
14941     case EL_EMERALD_PURPLE:
14942     case EL_SP_INFOTRON:
14943       RaiseScore(level.score[SC_EMERALD]);
14944       break;
14945     case EL_DIAMOND:
14946       RaiseScore(level.score[SC_DIAMOND]);
14947       break;
14948     case EL_CRYSTAL:
14949       RaiseScore(level.score[SC_CRYSTAL]);
14950       break;
14951     case EL_PEARL:
14952       RaiseScore(level.score[SC_PEARL]);
14953       break;
14954     case EL_BUG:
14955     case EL_BD_BUTTERFLY:
14956     case EL_SP_ELECTRON:
14957       RaiseScore(level.score[SC_BUG]);
14958       break;
14959     case EL_SPACESHIP:
14960     case EL_BD_FIREFLY:
14961     case EL_SP_SNIKSNAK:
14962       RaiseScore(level.score[SC_SPACESHIP]);
14963       break;
14964     case EL_YAMYAM:
14965     case EL_DARK_YAMYAM:
14966       RaiseScore(level.score[SC_YAMYAM]);
14967       break;
14968     case EL_ROBOT:
14969       RaiseScore(level.score[SC_ROBOT]);
14970       break;
14971     case EL_PACMAN:
14972       RaiseScore(level.score[SC_PACMAN]);
14973       break;
14974     case EL_NUT:
14975       RaiseScore(level.score[SC_NUT]);
14976       break;
14977     case EL_DYNAMITE:
14978     case EL_EM_DYNAMITE:
14979     case EL_SP_DISK_RED:
14980     case EL_DYNABOMB_INCREASE_NUMBER:
14981     case EL_DYNABOMB_INCREASE_SIZE:
14982     case EL_DYNABOMB_INCREASE_POWER:
14983       RaiseScore(level.score[SC_DYNAMITE]);
14984       break;
14985     case EL_SHIELD_NORMAL:
14986     case EL_SHIELD_DEADLY:
14987       RaiseScore(level.score[SC_SHIELD]);
14988       break;
14989     case EL_EXTRA_TIME:
14990       RaiseScore(level.extra_time_score);
14991       break;
14992     case EL_KEY_1:
14993     case EL_KEY_2:
14994     case EL_KEY_3:
14995     case EL_KEY_4:
14996     case EL_EM_KEY_1:
14997     case EL_EM_KEY_2:
14998     case EL_EM_KEY_3:
14999     case EL_EM_KEY_4:
15000     case EL_EMC_KEY_5:
15001     case EL_EMC_KEY_6:
15002     case EL_EMC_KEY_7:
15003     case EL_EMC_KEY_8:
15004     case EL_DC_KEY_WHITE:
15005       RaiseScore(level.score[SC_KEY]);
15006       break;
15007     default:
15008       RaiseScore(element_info[element].collect_score);
15009       break;
15010   }
15011 }
15012
15013 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15014 {
15015   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15016   {
15017     // closing door required in case of envelope style request dialogs
15018     if (!skip_request)
15019     {
15020       // prevent short reactivation of overlay buttons while closing door
15021       SetOverlayActive(FALSE);
15022
15023       CloseDoor(DOOR_CLOSE_1);
15024     }
15025
15026     if (network.enabled)
15027       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15028     else
15029     {
15030       if (quick_quit)
15031         FadeSkipNextFadeIn();
15032
15033       SetGameStatus(GAME_MODE_MAIN);
15034
15035       DrawMainMenu();
15036     }
15037   }
15038   else          // continue playing the game
15039   {
15040     if (tape.playing && tape.deactivate_display)
15041       TapeDeactivateDisplayOff(TRUE);
15042
15043     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15044
15045     if (tape.playing && tape.deactivate_display)
15046       TapeDeactivateDisplayOn();
15047   }
15048 }
15049
15050 void RequestQuitGame(boolean ask_if_really_quit)
15051 {
15052   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15053   boolean skip_request = game.all_players_gone || quick_quit;
15054
15055   RequestQuitGameExt(skip_request, quick_quit,
15056                      "Do you really want to quit the game?");
15057 }
15058
15059 void RequestRestartGame(char *message)
15060 {
15061   game.restart_game_message = NULL;
15062
15063   boolean has_started_game = hasStartedNetworkGame();
15064   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15065
15066   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15067   {
15068     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15069   }
15070   else
15071   {
15072     SetGameStatus(GAME_MODE_MAIN);
15073
15074     DrawMainMenu();
15075   }
15076 }
15077
15078 void CheckGameOver(void)
15079 {
15080   static boolean last_game_over = FALSE;
15081   static int game_over_delay = 0;
15082   int game_over_delay_value = 50;
15083   boolean game_over = checkGameFailed();
15084
15085   // do not handle game over if request dialog is already active
15086   if (game.request_active)
15087     return;
15088
15089   // do not ask to play again if game was never actually played
15090   if (!game.GamePlayed)
15091     return;
15092
15093   if (!game_over)
15094   {
15095     last_game_over = FALSE;
15096     game_over_delay = game_over_delay_value;
15097
15098     return;
15099   }
15100
15101   if (game_over_delay > 0)
15102   {
15103     game_over_delay--;
15104
15105     return;
15106   }
15107
15108   if (last_game_over != game_over)
15109     game.restart_game_message = (hasStartedNetworkGame() ?
15110                                  "Game over! Play it again?" :
15111                                  "Game over!");
15112
15113   last_game_over = game_over;
15114 }
15115
15116 boolean checkGameSolved(void)
15117 {
15118   // set for all game engines if level was solved
15119   return game.LevelSolved_GameEnd;
15120 }
15121
15122 boolean checkGameFailed(void)
15123 {
15124   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15125     return (game_em.game_over && !game_em.level_solved);
15126   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15127     return (game_sp.game_over && !game_sp.level_solved);
15128   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15129     return (game_mm.game_over && !game_mm.level_solved);
15130   else                          // GAME_ENGINE_TYPE_RND
15131     return (game.GameOver && !game.LevelSolved);
15132 }
15133
15134 boolean checkGameEnded(void)
15135 {
15136   return (checkGameSolved() || checkGameFailed());
15137 }
15138
15139
15140 // ----------------------------------------------------------------------------
15141 // random generator functions
15142 // ----------------------------------------------------------------------------
15143
15144 unsigned int InitEngineRandom_RND(int seed)
15145 {
15146   game.num_random_calls = 0;
15147
15148   return InitEngineRandom(seed);
15149 }
15150
15151 unsigned int RND(int max)
15152 {
15153   if (max > 0)
15154   {
15155     game.num_random_calls++;
15156
15157     return GetEngineRandom(max);
15158   }
15159
15160   return 0;
15161 }
15162
15163
15164 // ----------------------------------------------------------------------------
15165 // game engine snapshot handling functions
15166 // ----------------------------------------------------------------------------
15167
15168 struct EngineSnapshotInfo
15169 {
15170   // runtime values for custom element collect score
15171   int collect_score[NUM_CUSTOM_ELEMENTS];
15172
15173   // runtime values for group element choice position
15174   int choice_pos[NUM_GROUP_ELEMENTS];
15175
15176   // runtime values for belt position animations
15177   int belt_graphic[4][NUM_BELT_PARTS];
15178   int belt_anim_mode[4][NUM_BELT_PARTS];
15179 };
15180
15181 static struct EngineSnapshotInfo engine_snapshot_rnd;
15182 static char *snapshot_level_identifier = NULL;
15183 static int snapshot_level_nr = -1;
15184
15185 static void SaveEngineSnapshotValues_RND(void)
15186 {
15187   static int belt_base_active_element[4] =
15188   {
15189     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15190     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15191     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15192     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15193   };
15194   int i, j;
15195
15196   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15197   {
15198     int element = EL_CUSTOM_START + i;
15199
15200     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15201   }
15202
15203   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15204   {
15205     int element = EL_GROUP_START + i;
15206
15207     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15208   }
15209
15210   for (i = 0; i < 4; i++)
15211   {
15212     for (j = 0; j < NUM_BELT_PARTS; j++)
15213     {
15214       int element = belt_base_active_element[i] + j;
15215       int graphic = el2img(element);
15216       int anim_mode = graphic_info[graphic].anim_mode;
15217
15218       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15219       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15220     }
15221   }
15222 }
15223
15224 static void LoadEngineSnapshotValues_RND(void)
15225 {
15226   unsigned int num_random_calls = game.num_random_calls;
15227   int i, j;
15228
15229   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15230   {
15231     int element = EL_CUSTOM_START + i;
15232
15233     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15234   }
15235
15236   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15237   {
15238     int element = EL_GROUP_START + i;
15239
15240     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15241   }
15242
15243   for (i = 0; i < 4; i++)
15244   {
15245     for (j = 0; j < NUM_BELT_PARTS; j++)
15246     {
15247       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15248       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15249
15250       graphic_info[graphic].anim_mode = anim_mode;
15251     }
15252   }
15253
15254   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15255   {
15256     InitRND(tape.random_seed);
15257     for (i = 0; i < num_random_calls; i++)
15258       RND(1);
15259   }
15260
15261   if (game.num_random_calls != num_random_calls)
15262   {
15263     Error(ERR_INFO, "number of random calls out of sync");
15264     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15265     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15266     Error(ERR_EXIT, "this should not happen -- please debug");
15267   }
15268 }
15269
15270 void FreeEngineSnapshotSingle(void)
15271 {
15272   FreeSnapshotSingle();
15273
15274   setString(&snapshot_level_identifier, NULL);
15275   snapshot_level_nr = -1;
15276 }
15277
15278 void FreeEngineSnapshotList(void)
15279 {
15280   FreeSnapshotList();
15281 }
15282
15283 static ListNode *SaveEngineSnapshotBuffers(void)
15284 {
15285   ListNode *buffers = NULL;
15286
15287   // copy some special values to a structure better suited for the snapshot
15288
15289   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15290     SaveEngineSnapshotValues_RND();
15291   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15292     SaveEngineSnapshotValues_EM();
15293   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15294     SaveEngineSnapshotValues_SP(&buffers);
15295   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15296     SaveEngineSnapshotValues_MM(&buffers);
15297
15298   // save values stored in special snapshot structure
15299
15300   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15301     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15302   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15303     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15304   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15305     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15306   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15307     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15308
15309   // save further RND engine values
15310
15311   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15312   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15313   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15314
15315   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15316   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15317   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15318   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15319   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15320
15321   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15322   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15323   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15324
15325   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15326
15327   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15328   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15329
15330   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15331   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15332   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15333   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15334   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15335   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15336   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15337   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15338   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15339   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15340   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15341   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15342   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15343   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15344   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15345   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15346   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15347   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15348
15349   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15350   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15351
15352   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15353   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15354   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15355
15356   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15357   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15358
15359   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15360   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15361   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15362   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15363   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15364
15365   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15366   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15367
15368 #if 0
15369   ListNode *node = engine_snapshot_list_rnd;
15370   int num_bytes = 0;
15371
15372   while (node != NULL)
15373   {
15374     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15375
15376     node = node->next;
15377   }
15378
15379   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15380 #endif
15381
15382   return buffers;
15383 }
15384
15385 void SaveEngineSnapshotSingle(void)
15386 {
15387   ListNode *buffers = SaveEngineSnapshotBuffers();
15388
15389   // finally save all snapshot buffers to single snapshot
15390   SaveSnapshotSingle(buffers);
15391
15392   // save level identification information
15393   setString(&snapshot_level_identifier, leveldir_current->identifier);
15394   snapshot_level_nr = level_nr;
15395 }
15396
15397 boolean CheckSaveEngineSnapshotToList(void)
15398 {
15399   boolean save_snapshot =
15400     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15401      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15402       game.snapshot.changed_action) ||
15403      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15404       game.snapshot.collected_item));
15405
15406   game.snapshot.changed_action = FALSE;
15407   game.snapshot.collected_item = FALSE;
15408   game.snapshot.save_snapshot = save_snapshot;
15409
15410   return save_snapshot;
15411 }
15412
15413 void SaveEngineSnapshotToList(void)
15414 {
15415   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15416       tape.quick_resume)
15417     return;
15418
15419   ListNode *buffers = SaveEngineSnapshotBuffers();
15420
15421   // finally save all snapshot buffers to snapshot list
15422   SaveSnapshotToList(buffers);
15423 }
15424
15425 void SaveEngineSnapshotToListInitial(void)
15426 {
15427   FreeEngineSnapshotList();
15428
15429   SaveEngineSnapshotToList();
15430 }
15431
15432 static void LoadEngineSnapshotValues(void)
15433 {
15434   // restore special values from snapshot structure
15435
15436   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15437     LoadEngineSnapshotValues_RND();
15438   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15439     LoadEngineSnapshotValues_EM();
15440   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15441     LoadEngineSnapshotValues_SP();
15442   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15443     LoadEngineSnapshotValues_MM();
15444 }
15445
15446 void LoadEngineSnapshotSingle(void)
15447 {
15448   LoadSnapshotSingle();
15449
15450   LoadEngineSnapshotValues();
15451 }
15452
15453 static void LoadEngineSnapshot_Undo(int steps)
15454 {
15455   LoadSnapshotFromList_Older(steps);
15456
15457   LoadEngineSnapshotValues();
15458 }
15459
15460 static void LoadEngineSnapshot_Redo(int steps)
15461 {
15462   LoadSnapshotFromList_Newer(steps);
15463
15464   LoadEngineSnapshotValues();
15465 }
15466
15467 boolean CheckEngineSnapshotSingle(void)
15468 {
15469   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15470           snapshot_level_nr == level_nr);
15471 }
15472
15473 boolean CheckEngineSnapshotList(void)
15474 {
15475   return CheckSnapshotList();
15476 }
15477
15478
15479 // ---------- new game button stuff -------------------------------------------
15480
15481 static struct
15482 {
15483   int graphic;
15484   struct XY *pos;
15485   int gadget_id;
15486   boolean *setup_value;
15487   boolean allowed_on_tape;
15488   boolean is_touch_button;
15489   char *infotext;
15490 } gamebutton_info[NUM_GAME_BUTTONS] =
15491 {
15492   {
15493     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15494     GAME_CTRL_ID_STOP,                          NULL,
15495     TRUE, FALSE,                                "stop game"
15496   },
15497   {
15498     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15499     GAME_CTRL_ID_PAUSE,                         NULL,
15500     TRUE, FALSE,                                "pause game"
15501   },
15502   {
15503     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15504     GAME_CTRL_ID_PLAY,                          NULL,
15505     TRUE, FALSE,                                "play game"
15506   },
15507   {
15508     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15509     GAME_CTRL_ID_UNDO,                          NULL,
15510     TRUE, FALSE,                                "undo step"
15511   },
15512   {
15513     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15514     GAME_CTRL_ID_REDO,                          NULL,
15515     TRUE, FALSE,                                "redo step"
15516   },
15517   {
15518     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15519     GAME_CTRL_ID_SAVE,                          NULL,
15520     TRUE, FALSE,                                "save game"
15521   },
15522   {
15523     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15524     GAME_CTRL_ID_PAUSE2,                        NULL,
15525     TRUE, FALSE,                                "pause game"
15526   },
15527   {
15528     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15529     GAME_CTRL_ID_LOAD,                          NULL,
15530     TRUE, FALSE,                                "load game"
15531   },
15532   {
15533     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15534     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15535     FALSE, FALSE,                               "stop game"
15536   },
15537   {
15538     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15539     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15540     FALSE, FALSE,                               "pause game"
15541   },
15542   {
15543     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15544     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15545     FALSE, FALSE,                               "play game"
15546   },
15547   {
15548     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15549     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15550     FALSE, TRUE,                                "stop game"
15551   },
15552   {
15553     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15554     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15555     FALSE, TRUE,                                "pause game"
15556   },
15557   {
15558     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15559     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15560     TRUE, FALSE,                                "background music on/off"
15561   },
15562   {
15563     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15564     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15565     TRUE, FALSE,                                "sound loops on/off"
15566   },
15567   {
15568     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15569     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15570     TRUE, FALSE,                                "normal sounds on/off"
15571   },
15572   {
15573     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15574     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15575     FALSE, FALSE,                               "background music on/off"
15576   },
15577   {
15578     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15579     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15580     FALSE, FALSE,                               "sound loops on/off"
15581   },
15582   {
15583     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15584     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15585     FALSE, FALSE,                               "normal sounds on/off"
15586   }
15587 };
15588
15589 void CreateGameButtons(void)
15590 {
15591   int i;
15592
15593   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15594   {
15595     int graphic = gamebutton_info[i].graphic;
15596     struct GraphicInfo *gfx = &graphic_info[graphic];
15597     struct XY *pos = gamebutton_info[i].pos;
15598     struct GadgetInfo *gi;
15599     int button_type;
15600     boolean checked;
15601     unsigned int event_mask;
15602     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15603     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15604     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15605     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15606     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15607     int gd_x   = gfx->src_x;
15608     int gd_y   = gfx->src_y;
15609     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15610     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15611     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15612     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15613     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15614     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15615     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15616     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15617     int id = i;
15618
15619     if (gfx->bitmap == NULL)
15620     {
15621       game_gadget[id] = NULL;
15622
15623       continue;
15624     }
15625
15626     if (id == GAME_CTRL_ID_STOP ||
15627         id == GAME_CTRL_ID_PANEL_STOP ||
15628         id == GAME_CTRL_ID_TOUCH_STOP ||
15629         id == GAME_CTRL_ID_PLAY ||
15630         id == GAME_CTRL_ID_PANEL_PLAY ||
15631         id == GAME_CTRL_ID_SAVE ||
15632         id == GAME_CTRL_ID_LOAD)
15633     {
15634       button_type = GD_TYPE_NORMAL_BUTTON;
15635       checked = FALSE;
15636       event_mask = GD_EVENT_RELEASED;
15637     }
15638     else if (id == GAME_CTRL_ID_UNDO ||
15639              id == GAME_CTRL_ID_REDO)
15640     {
15641       button_type = GD_TYPE_NORMAL_BUTTON;
15642       checked = FALSE;
15643       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15644     }
15645     else
15646     {
15647       button_type = GD_TYPE_CHECK_BUTTON;
15648       checked = (gamebutton_info[i].setup_value != NULL ?
15649                  *gamebutton_info[i].setup_value : FALSE);
15650       event_mask = GD_EVENT_PRESSED;
15651     }
15652
15653     gi = CreateGadget(GDI_CUSTOM_ID, id,
15654                       GDI_IMAGE_ID, graphic,
15655                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15656                       GDI_X, base_x + x,
15657                       GDI_Y, base_y + y,
15658                       GDI_WIDTH, gfx->width,
15659                       GDI_HEIGHT, gfx->height,
15660                       GDI_TYPE, button_type,
15661                       GDI_STATE, GD_BUTTON_UNPRESSED,
15662                       GDI_CHECKED, checked,
15663                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15664                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15665                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15666                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15667                       GDI_DIRECT_DRAW, FALSE,
15668                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15669                       GDI_EVENT_MASK, event_mask,
15670                       GDI_CALLBACK_ACTION, HandleGameButtons,
15671                       GDI_END);
15672
15673     if (gi == NULL)
15674       Error(ERR_EXIT, "cannot create gadget");
15675
15676     game_gadget[id] = gi;
15677   }
15678 }
15679
15680 void FreeGameButtons(void)
15681 {
15682   int i;
15683
15684   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15685     FreeGadget(game_gadget[i]);
15686 }
15687
15688 static void UnmapGameButtonsAtSamePosition(int id)
15689 {
15690   int i;
15691
15692   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15693     if (i != id &&
15694         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15695         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15696       UnmapGadget(game_gadget[i]);
15697 }
15698
15699 static void UnmapGameButtonsAtSamePosition_All(void)
15700 {
15701   if (setup.show_snapshot_buttons)
15702   {
15703     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15704     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15705     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15706   }
15707   else
15708   {
15709     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15710     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15711     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15712
15713     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15714     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15715     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15716   }
15717 }
15718
15719 static void MapGameButtonsAtSamePosition(int id)
15720 {
15721   int i;
15722
15723   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15724     if (i != id &&
15725         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15726         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15727       MapGadget(game_gadget[i]);
15728
15729   UnmapGameButtonsAtSamePosition_All();
15730 }
15731
15732 void MapUndoRedoButtons(void)
15733 {
15734   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15735   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15736
15737   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15738   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15739 }
15740
15741 void UnmapUndoRedoButtons(void)
15742 {
15743   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15744   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15745
15746   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15747   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15748 }
15749
15750 void ModifyPauseButtons(void)
15751 {
15752   static int ids[] =
15753   {
15754     GAME_CTRL_ID_PAUSE,
15755     GAME_CTRL_ID_PAUSE2,
15756     GAME_CTRL_ID_PANEL_PAUSE,
15757     GAME_CTRL_ID_TOUCH_PAUSE,
15758     -1
15759   };
15760   int i;
15761
15762   for (i = 0; ids[i] > -1; i++)
15763     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15764 }
15765
15766 static void MapGameButtonsExt(boolean on_tape)
15767 {
15768   int i;
15769
15770   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15771     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15772         i != GAME_CTRL_ID_UNDO &&
15773         i != GAME_CTRL_ID_REDO)
15774       MapGadget(game_gadget[i]);
15775
15776   UnmapGameButtonsAtSamePosition_All();
15777
15778   RedrawGameButtons();
15779 }
15780
15781 static void UnmapGameButtonsExt(boolean on_tape)
15782 {
15783   int i;
15784
15785   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15786     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15787       UnmapGadget(game_gadget[i]);
15788 }
15789
15790 static void RedrawGameButtonsExt(boolean on_tape)
15791 {
15792   int i;
15793
15794   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15795     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15796       RedrawGadget(game_gadget[i]);
15797 }
15798
15799 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15800 {
15801   if (gi == NULL)
15802     return;
15803
15804   gi->checked = state;
15805 }
15806
15807 static void RedrawSoundButtonGadget(int id)
15808 {
15809   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15810              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15811              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15812              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15813              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15814              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15815              id);
15816
15817   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15818   RedrawGadget(game_gadget[id2]);
15819 }
15820
15821 void MapGameButtons(void)
15822 {
15823   MapGameButtonsExt(FALSE);
15824 }
15825
15826 void UnmapGameButtons(void)
15827 {
15828   UnmapGameButtonsExt(FALSE);
15829 }
15830
15831 void RedrawGameButtons(void)
15832 {
15833   RedrawGameButtonsExt(FALSE);
15834 }
15835
15836 void MapGameButtonsOnTape(void)
15837 {
15838   MapGameButtonsExt(TRUE);
15839 }
15840
15841 void UnmapGameButtonsOnTape(void)
15842 {
15843   UnmapGameButtonsExt(TRUE);
15844 }
15845
15846 void RedrawGameButtonsOnTape(void)
15847 {
15848   RedrawGameButtonsExt(TRUE);
15849 }
15850
15851 static void GameUndoRedoExt(void)
15852 {
15853   ClearPlayerAction();
15854
15855   tape.pausing = TRUE;
15856
15857   RedrawPlayfield();
15858   UpdateAndDisplayGameControlValues();
15859
15860   DrawCompleteVideoDisplay();
15861   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15862   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15863   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15864
15865   BackToFront();
15866 }
15867
15868 static void GameUndo(int steps)
15869 {
15870   if (!CheckEngineSnapshotList())
15871     return;
15872
15873   LoadEngineSnapshot_Undo(steps);
15874
15875   GameUndoRedoExt();
15876 }
15877
15878 static void GameRedo(int steps)
15879 {
15880   if (!CheckEngineSnapshotList())
15881     return;
15882
15883   LoadEngineSnapshot_Redo(steps);
15884
15885   GameUndoRedoExt();
15886 }
15887
15888 static void HandleGameButtonsExt(int id, int button)
15889 {
15890   static boolean game_undo_executed = FALSE;
15891   int steps = BUTTON_STEPSIZE(button);
15892   boolean handle_game_buttons =
15893     (game_status == GAME_MODE_PLAYING ||
15894      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15895
15896   if (!handle_game_buttons)
15897     return;
15898
15899   switch (id)
15900   {
15901     case GAME_CTRL_ID_STOP:
15902     case GAME_CTRL_ID_PANEL_STOP:
15903     case GAME_CTRL_ID_TOUCH_STOP:
15904       if (game_status == GAME_MODE_MAIN)
15905         break;
15906
15907       if (tape.playing)
15908         TapeStop();
15909       else
15910         RequestQuitGame(TRUE);
15911
15912       break;
15913
15914     case GAME_CTRL_ID_PAUSE:
15915     case GAME_CTRL_ID_PAUSE2:
15916     case GAME_CTRL_ID_PANEL_PAUSE:
15917     case GAME_CTRL_ID_TOUCH_PAUSE:
15918       if (network.enabled && game_status == GAME_MODE_PLAYING)
15919       {
15920         if (tape.pausing)
15921           SendToServer_ContinuePlaying();
15922         else
15923           SendToServer_PausePlaying();
15924       }
15925       else
15926         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15927
15928       game_undo_executed = FALSE;
15929
15930       break;
15931
15932     case GAME_CTRL_ID_PLAY:
15933     case GAME_CTRL_ID_PANEL_PLAY:
15934       if (game_status == GAME_MODE_MAIN)
15935       {
15936         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15937       }
15938       else if (tape.pausing)
15939       {
15940         if (network.enabled)
15941           SendToServer_ContinuePlaying();
15942         else
15943           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15944       }
15945       break;
15946
15947     case GAME_CTRL_ID_UNDO:
15948       // Important: When using "save snapshot when collecting an item" mode,
15949       // load last (current) snapshot for first "undo" after pressing "pause"
15950       // (else the last-but-one snapshot would be loaded, because the snapshot
15951       // pointer already points to the last snapshot when pressing "pause",
15952       // which is fine for "every step/move" mode, but not for "every collect")
15953       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15954           !game_undo_executed)
15955         steps--;
15956
15957       game_undo_executed = TRUE;
15958
15959       GameUndo(steps);
15960       break;
15961
15962     case GAME_CTRL_ID_REDO:
15963       GameRedo(steps);
15964       break;
15965
15966     case GAME_CTRL_ID_SAVE:
15967       TapeQuickSave();
15968       break;
15969
15970     case GAME_CTRL_ID_LOAD:
15971       TapeQuickLoad();
15972       break;
15973
15974     case SOUND_CTRL_ID_MUSIC:
15975     case SOUND_CTRL_ID_PANEL_MUSIC:
15976       if (setup.sound_music)
15977       { 
15978         setup.sound_music = FALSE;
15979
15980         FadeMusic();
15981       }
15982       else if (audio.music_available)
15983       { 
15984         setup.sound = setup.sound_music = TRUE;
15985
15986         SetAudioMode(setup.sound);
15987
15988         if (game_status == GAME_MODE_PLAYING)
15989           PlayLevelMusic();
15990       }
15991
15992       RedrawSoundButtonGadget(id);
15993
15994       break;
15995
15996     case SOUND_CTRL_ID_LOOPS:
15997     case SOUND_CTRL_ID_PANEL_LOOPS:
15998       if (setup.sound_loops)
15999         setup.sound_loops = FALSE;
16000       else if (audio.loops_available)
16001       {
16002         setup.sound = setup.sound_loops = TRUE;
16003
16004         SetAudioMode(setup.sound);
16005       }
16006
16007       RedrawSoundButtonGadget(id);
16008
16009       break;
16010
16011     case SOUND_CTRL_ID_SIMPLE:
16012     case SOUND_CTRL_ID_PANEL_SIMPLE:
16013       if (setup.sound_simple)
16014         setup.sound_simple = FALSE;
16015       else if (audio.sound_available)
16016       {
16017         setup.sound = setup.sound_simple = TRUE;
16018
16019         SetAudioMode(setup.sound);
16020       }
16021
16022       RedrawSoundButtonGadget(id);
16023
16024       break;
16025
16026     default:
16027       break;
16028   }
16029 }
16030
16031 static void HandleGameButtons(struct GadgetInfo *gi)
16032 {
16033   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16034 }
16035
16036 void HandleSoundButtonKeys(Key key)
16037 {
16038   if (key == setup.shortcut.sound_simple)
16039     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16040   else if (key == setup.shortcut.sound_loops)
16041     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16042   else if (key == setup.shortcut.sound_music)
16043     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16044 }