small code cleanup
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void IncrementSokobanFieldsNeeded(void)
1687 {
1688   if (level.sb_fields_needed)
1689     game.sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementSokobanObjectsNeeded(void)
1693 {
1694   if (level.sb_objects_needed)
1695     game.sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementSokobanFieldsNeeded(void)
1699 {
1700   if (game.sokoban_fields_still_needed > 0)
1701     game.sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementSokobanObjectsNeeded(void)
1705 {
1706   if (game.sokoban_objects_still_needed > 0)
1707     game.sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       game.lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       game.friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (game.LevelSolved ?
2211               game.LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (game.LevelSolved ?
2220                game.LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                game.score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               game.gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      game.gems_still_needed > 0 ||
2243                      game.sokoban_fields_still_needed > 0 ||
2244                      game.sokoban_objects_still_needed > 0 ||
2245                      game.lights_still_needed > 0);
2246   int health = (game.LevelSolved ?
2247                 game.LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 game.health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     game.friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     game.sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     game.sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckFadeAll())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   if (!level_editor_test_game)
3369     FadeOut(fade_mask);
3370
3371   // needed if different viewport properties defined for playing
3372   ChangeViewportPropertiesIfNeeded();
3373
3374   ClearField();
3375
3376   DrawCompleteVideoDisplay();
3377
3378   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3379
3380   InitGameEngine();
3381   InitGameControlValues();
3382
3383   // don't play tapes over network
3384   network_playing = (network.enabled && !tape.playing);
3385
3386   for (i = 0; i < MAX_PLAYERS; i++)
3387   {
3388     struct PlayerInfo *player = &stored_player[i];
3389
3390     player->index_nr = i;
3391     player->index_bit = (1 << i);
3392     player->element_nr = EL_PLAYER_1 + i;
3393
3394     player->present = FALSE;
3395     player->active = FALSE;
3396     player->mapped = FALSE;
3397
3398     player->killed = FALSE;
3399     player->reanimated = FALSE;
3400     player->buried = FALSE;
3401
3402     player->action = 0;
3403     player->effective_action = 0;
3404     player->programmed_action = 0;
3405     player->snap_action = 0;
3406
3407     player->mouse_action.lx = 0;
3408     player->mouse_action.ly = 0;
3409     player->mouse_action.button = 0;
3410     player->mouse_action.button_hint = 0;
3411
3412     player->effective_mouse_action.lx = 0;
3413     player->effective_mouse_action.ly = 0;
3414     player->effective_mouse_action.button = 0;
3415     player->effective_mouse_action.button_hint = 0;
3416
3417     for (j = 0; j < MAX_NUM_KEYS; j++)
3418       player->key[j] = FALSE;
3419
3420     player->num_white_keys = 0;
3421
3422     player->dynabomb_count = 0;
3423     player->dynabomb_size = 1;
3424     player->dynabombs_left = 0;
3425     player->dynabomb_xl = FALSE;
3426
3427     player->MovDir = initial_move_dir;
3428     player->MovPos = 0;
3429     player->GfxPos = 0;
3430     player->GfxDir = initial_move_dir;
3431     player->GfxAction = ACTION_DEFAULT;
3432     player->Frame = 0;
3433     player->StepFrame = 0;
3434
3435     player->initial_element = player->element_nr;
3436     player->artwork_element =
3437       (level.use_artwork_element[i] ? level.artwork_element[i] :
3438        player->element_nr);
3439     player->use_murphy = FALSE;
3440
3441     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3442     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3443
3444     player->gravity = level.initial_player_gravity[i];
3445
3446     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3447
3448     player->actual_frame_counter = 0;
3449
3450     player->step_counter = 0;
3451
3452     player->last_move_dir = initial_move_dir;
3453
3454     player->is_active = FALSE;
3455
3456     player->is_waiting = FALSE;
3457     player->is_moving = FALSE;
3458     player->is_auto_moving = FALSE;
3459     player->is_digging = FALSE;
3460     player->is_snapping = FALSE;
3461     player->is_collecting = FALSE;
3462     player->is_pushing = FALSE;
3463     player->is_switching = FALSE;
3464     player->is_dropping = FALSE;
3465     player->is_dropping_pressed = FALSE;
3466
3467     player->is_bored = FALSE;
3468     player->is_sleeping = FALSE;
3469
3470     player->was_waiting = TRUE;
3471     player->was_moving = FALSE;
3472     player->was_snapping = FALSE;
3473     player->was_dropping = FALSE;
3474
3475     player->force_dropping = FALSE;
3476
3477     player->frame_counter_bored = -1;
3478     player->frame_counter_sleeping = -1;
3479
3480     player->anim_delay_counter = 0;
3481     player->post_delay_counter = 0;
3482
3483     player->dir_waiting = initial_move_dir;
3484     player->action_waiting = ACTION_DEFAULT;
3485     player->last_action_waiting = ACTION_DEFAULT;
3486     player->special_action_bored = ACTION_DEFAULT;
3487     player->special_action_sleeping = ACTION_DEFAULT;
3488
3489     player->switch_x = -1;
3490     player->switch_y = -1;
3491
3492     player->drop_x = -1;
3493     player->drop_y = -1;
3494
3495     player->show_envelope = 0;
3496
3497     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3498
3499     player->push_delay       = -1;      // initialized when pushing starts
3500     player->push_delay_value = game.initial_push_delay_value;
3501
3502     player->drop_delay = 0;
3503     player->drop_pressed_delay = 0;
3504
3505     player->last_jx = -1;
3506     player->last_jy = -1;
3507     player->jx = -1;
3508     player->jy = -1;
3509
3510     player->shield_normal_time_left = 0;
3511     player->shield_deadly_time_left = 0;
3512
3513     player->inventory_infinite_element = EL_UNDEFINED;
3514     player->inventory_size = 0;
3515
3516     if (level.use_initial_inventory[i])
3517     {
3518       for (j = 0; j < level.initial_inventory_size[i]; j++)
3519       {
3520         int element = level.initial_inventory_content[i][j];
3521         int collect_count = element_info[element].collect_count_initial;
3522         int k;
3523
3524         if (!IS_CUSTOM_ELEMENT(element))
3525           collect_count = 1;
3526
3527         if (collect_count == 0)
3528           player->inventory_infinite_element = element;
3529         else
3530           for (k = 0; k < collect_count; k++)
3531             if (player->inventory_size < MAX_INVENTORY_SIZE)
3532               player->inventory_element[player->inventory_size++] = element;
3533       }
3534     }
3535
3536     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3537     SnapField(player, 0, 0);
3538
3539     map_player_action[i] = i;
3540   }
3541
3542   network_player_action_received = FALSE;
3543
3544   // initial null action
3545   if (network_playing)
3546     SendToServer_MovePlayer(MV_NONE);
3547
3548   FrameCounter = 0;
3549   TimeFrames = 0;
3550   TimePlayed = 0;
3551   TimeLeft = level.time;
3552   TapeTime = 0;
3553
3554   ScreenMovDir = MV_NONE;
3555   ScreenMovPos = 0;
3556   ScreenGfxPos = 0;
3557
3558   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3559
3560   game.robot_wheel_x = -1;
3561   game.robot_wheel_y = -1;
3562
3563   game.exit_x = -1;
3564   game.exit_y = -1;
3565
3566   game.all_players_gone = FALSE;
3567
3568   game.LevelSolved = FALSE;
3569   game.GameOver = FALSE;
3570
3571   game.GamePlayed = !tape.playing;
3572
3573   game.LevelSolved_GameWon = FALSE;
3574   game.LevelSolved_GameEnd = FALSE;
3575   game.LevelSolved_SaveTape = FALSE;
3576   game.LevelSolved_SaveScore = FALSE;
3577
3578   game.LevelSolved_CountingTime = 0;
3579   game.LevelSolved_CountingScore = 0;
3580   game.LevelSolved_CountingHealth = 0;
3581
3582   game.panel.active = TRUE;
3583
3584   game.no_time_limit = (level.time == 0);
3585
3586   game.yamyam_content_nr = 0;
3587   game.robot_wheel_active = FALSE;
3588   game.magic_wall_active = FALSE;
3589   game.magic_wall_time_left = 0;
3590   game.light_time_left = 0;
3591   game.timegate_time_left = 0;
3592   game.switchgate_pos = 0;
3593   game.wind_direction = level.wind_direction_initial;
3594
3595   game.score = 0;
3596   game.score_final = 0;
3597
3598   game.health = MAX_HEALTH;
3599   game.health_final = MAX_HEALTH;
3600
3601   game.gems_still_needed = level.gems_needed;
3602   game.sokoban_fields_still_needed = 0;
3603   game.sokoban_objects_still_needed = 0;
3604   game.lights_still_needed = 0;
3605   game.players_still_needed = 0;
3606   game.friends_still_needed = 0;
3607
3608   game.lenses_time_left = 0;
3609   game.magnify_time_left = 0;
3610
3611   game.ball_state = level.ball_state_initial;
3612   game.ball_content_nr = 0;
3613
3614   game.explosions_delayed = TRUE;
3615
3616   game.envelope_active = FALSE;
3617
3618   for (i = 0; i < NUM_BELTS; i++)
3619   {
3620     game.belt_dir[i] = MV_NONE;
3621     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3622   }
3623
3624   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3625     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3626
3627 #if DEBUG_INIT_PLAYER
3628   DebugPrintPlayerStatus("Player status at level initialization");
3629 #endif
3630
3631   SCAN_PLAYFIELD(x, y)
3632   {
3633     Feld[x][y] = Last[x][y] = level.field[x][y];
3634     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3635     ChangeDelay[x][y] = 0;
3636     ChangePage[x][y] = -1;
3637     CustomValue[x][y] = 0;              // initialized in InitField()
3638     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3639     AmoebaNr[x][y] = 0;
3640     WasJustMoving[x][y] = 0;
3641     WasJustFalling[x][y] = 0;
3642     CheckCollision[x][y] = 0;
3643     CheckImpact[x][y] = 0;
3644     Stop[x][y] = FALSE;
3645     Pushed[x][y] = FALSE;
3646
3647     ChangeCount[x][y] = 0;
3648     ChangeEvent[x][y] = -1;
3649
3650     ExplodePhase[x][y] = 0;
3651     ExplodeDelay[x][y] = 0;
3652     ExplodeField[x][y] = EX_TYPE_NONE;
3653
3654     RunnerVisit[x][y] = 0;
3655     PlayerVisit[x][y] = 0;
3656
3657     GfxFrame[x][y] = 0;
3658     GfxRandom[x][y] = INIT_GFX_RANDOM();
3659     GfxElement[x][y] = EL_UNDEFINED;
3660     GfxAction[x][y] = ACTION_DEFAULT;
3661     GfxDir[x][y] = MV_NONE;
3662     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3663   }
3664
3665   SCAN_PLAYFIELD(x, y)
3666   {
3667     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3668       emulate_bd = FALSE;
3669     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3670       emulate_sb = FALSE;
3671     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3672       emulate_sp = FALSE;
3673
3674     InitField(x, y, TRUE);
3675
3676     ResetGfxAnimation(x, y);
3677   }
3678
3679   InitBeltMovement();
3680
3681   for (i = 0; i < MAX_PLAYERS; i++)
3682   {
3683     struct PlayerInfo *player = &stored_player[i];
3684
3685     // set number of special actions for bored and sleeping animation
3686     player->num_special_action_bored =
3687       get_num_special_action(player->artwork_element,
3688                              ACTION_BORING_1, ACTION_BORING_LAST);
3689     player->num_special_action_sleeping =
3690       get_num_special_action(player->artwork_element,
3691                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3692   }
3693
3694   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3695                     emulate_sb ? EMU_SOKOBAN :
3696                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3697
3698   // initialize type of slippery elements
3699   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3700   {
3701     if (!IS_CUSTOM_ELEMENT(i))
3702     {
3703       // default: elements slip down either to the left or right randomly
3704       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3705
3706       // SP style elements prefer to slip down on the left side
3707       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3708         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3709
3710       // BD style elements prefer to slip down on the left side
3711       if (game.emulation == EMU_BOULDERDASH)
3712         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3713     }
3714   }
3715
3716   // initialize explosion and ignition delay
3717   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3718   {
3719     if (!IS_CUSTOM_ELEMENT(i))
3720     {
3721       int num_phase = 8;
3722       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3723                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3724                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3725       int last_phase = (num_phase + 1) * delay;
3726       int half_phase = (num_phase / 2) * delay;
3727
3728       element_info[i].explosion_delay = last_phase - 1;
3729       element_info[i].ignition_delay = half_phase;
3730
3731       if (i == EL_BLACK_ORB)
3732         element_info[i].ignition_delay = 1;
3733     }
3734   }
3735
3736   // correct non-moving belts to start moving left
3737   for (i = 0; i < NUM_BELTS; i++)
3738     if (game.belt_dir[i] == MV_NONE)
3739       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3740
3741 #if USE_NEW_PLAYER_ASSIGNMENTS
3742   // use preferred player also in local single-player mode
3743   if (!network.enabled && !game.team_mode)
3744   {
3745     int old_index_nr = local_player->index_nr;
3746     int new_index_nr = setup.network_player_nr;
3747
3748     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3749     {
3750       stored_player[old_index_nr].connected_locally = FALSE;
3751       stored_player[new_index_nr].connected_locally = TRUE;
3752     }
3753   }
3754
3755   for (i = 0; i < MAX_PLAYERS; i++)
3756   {
3757     stored_player[i].connected = FALSE;
3758
3759     // in network game mode, the local player might not be the first player
3760     if (stored_player[i].connected_locally)
3761       local_player = &stored_player[i];
3762   }
3763
3764   if (!network.enabled)
3765     local_player->connected = TRUE;
3766
3767   if (tape.playing)
3768   {
3769     for (i = 0; i < MAX_PLAYERS; i++)
3770       stored_player[i].connected = tape.player_participates[i];
3771   }
3772   else if (network.enabled)
3773   {
3774     // add team mode players connected over the network (needed for correct
3775     // assignment of player figures from level to locally playing players)
3776
3777     for (i = 0; i < MAX_PLAYERS; i++)
3778       if (stored_player[i].connected_network)
3779         stored_player[i].connected = TRUE;
3780   }
3781   else if (game.team_mode)
3782   {
3783     // try to guess locally connected team mode players (needed for correct
3784     // assignment of player figures from level to locally playing players)
3785
3786     for (i = 0; i < MAX_PLAYERS; i++)
3787       if (setup.input[i].use_joystick ||
3788           setup.input[i].key.left != KSYM_UNDEFINED)
3789         stored_player[i].connected = TRUE;
3790   }
3791
3792 #if DEBUG_INIT_PLAYER
3793   DebugPrintPlayerStatus("Player status after level initialization");
3794 #endif
3795
3796 #if DEBUG_INIT_PLAYER
3797   if (options.debug)
3798     printf("Reassigning players ...\n");
3799 #endif
3800
3801   // check if any connected player was not found in playfield
3802   for (i = 0; i < MAX_PLAYERS; i++)
3803   {
3804     struct PlayerInfo *player = &stored_player[i];
3805
3806     if (player->connected && !player->present)
3807     {
3808       struct PlayerInfo *field_player = NULL;
3809
3810 #if DEBUG_INIT_PLAYER
3811       if (options.debug)
3812         printf("- looking for field player for player %d ...\n", i + 1);
3813 #endif
3814
3815       // assign first free player found that is present in the playfield
3816
3817       // first try: look for unmapped playfield player that is not connected
3818       for (j = 0; j < MAX_PLAYERS; j++)
3819         if (field_player == NULL &&
3820             stored_player[j].present &&
3821             !stored_player[j].mapped &&
3822             !stored_player[j].connected)
3823           field_player = &stored_player[j];
3824
3825       // second try: look for *any* unmapped playfield player
3826       for (j = 0; j < MAX_PLAYERS; j++)
3827         if (field_player == NULL &&
3828             stored_player[j].present &&
3829             !stored_player[j].mapped)
3830           field_player = &stored_player[j];
3831
3832       if (field_player != NULL)
3833       {
3834         int jx = field_player->jx, jy = field_player->jy;
3835
3836 #if DEBUG_INIT_PLAYER
3837         if (options.debug)
3838           printf("- found player %d\n", field_player->index_nr + 1);
3839 #endif
3840
3841         player->present = FALSE;
3842         player->active = FALSE;
3843
3844         field_player->present = TRUE;
3845         field_player->active = TRUE;
3846
3847         /*
3848         player->initial_element = field_player->initial_element;
3849         player->artwork_element = field_player->artwork_element;
3850
3851         player->block_last_field       = field_player->block_last_field;
3852         player->block_delay_adjustment = field_player->block_delay_adjustment;
3853         */
3854
3855         StorePlayer[jx][jy] = field_player->element_nr;
3856
3857         field_player->jx = field_player->last_jx = jx;
3858         field_player->jy = field_player->last_jy = jy;
3859
3860         if (local_player == player)
3861           local_player = field_player;
3862
3863         map_player_action[field_player->index_nr] = i;
3864
3865         field_player->mapped = TRUE;
3866
3867 #if DEBUG_INIT_PLAYER
3868         if (options.debug)
3869           printf("- map_player_action[%d] == %d\n",
3870                  field_player->index_nr + 1, i + 1);
3871 #endif
3872       }
3873     }
3874
3875     if (player->connected && player->present)
3876       player->mapped = TRUE;
3877   }
3878
3879 #if DEBUG_INIT_PLAYER
3880   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3881 #endif
3882
3883 #else
3884
3885   // check if any connected player was not found in playfield
3886   for (i = 0; i < MAX_PLAYERS; i++)
3887   {
3888     struct PlayerInfo *player = &stored_player[i];
3889
3890     if (player->connected && !player->present)
3891     {
3892       for (j = 0; j < MAX_PLAYERS; j++)
3893       {
3894         struct PlayerInfo *field_player = &stored_player[j];
3895         int jx = field_player->jx, jy = field_player->jy;
3896
3897         // assign first free player found that is present in the playfield
3898         if (field_player->present && !field_player->connected)
3899         {
3900           player->present = TRUE;
3901           player->active = TRUE;
3902
3903           field_player->present = FALSE;
3904           field_player->active = FALSE;
3905
3906           player->initial_element = field_player->initial_element;
3907           player->artwork_element = field_player->artwork_element;
3908
3909           player->block_last_field       = field_player->block_last_field;
3910           player->block_delay_adjustment = field_player->block_delay_adjustment;
3911
3912           StorePlayer[jx][jy] = player->element_nr;
3913
3914           player->jx = player->last_jx = jx;
3915           player->jy = player->last_jy = jy;
3916
3917           break;
3918         }
3919       }
3920     }
3921   }
3922 #endif
3923
3924 #if 0
3925   printf("::: local_player->present == %d\n", local_player->present);
3926 #endif
3927
3928   // set focus to local player for network games, else to all players
3929   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3930   game.centered_player_nr_next = game.centered_player_nr;
3931   game.set_centered_player = FALSE;
3932
3933   if (network_playing && tape.recording)
3934   {
3935     // store client dependent player focus when recording network games
3936     tape.centered_player_nr_next = game.centered_player_nr_next;
3937     tape.set_centered_player = TRUE;
3938   }
3939
3940   if (tape.playing)
3941   {
3942     // when playing a tape, eliminate all players who do not participate
3943
3944 #if USE_NEW_PLAYER_ASSIGNMENTS
3945
3946     if (!game.team_mode)
3947     {
3948       for (i = 0; i < MAX_PLAYERS; i++)
3949       {
3950         if (stored_player[i].active &&
3951             !tape.player_participates[map_player_action[i]])
3952         {
3953           struct PlayerInfo *player = &stored_player[i];
3954           int jx = player->jx, jy = player->jy;
3955
3956 #if DEBUG_INIT_PLAYER
3957           if (options.debug)
3958             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3959 #endif
3960
3961           player->active = FALSE;
3962           StorePlayer[jx][jy] = 0;
3963           Feld[jx][jy] = EL_EMPTY;
3964         }
3965       }
3966     }
3967
3968 #else
3969
3970     for (i = 0; i < MAX_PLAYERS; i++)
3971     {
3972       if (stored_player[i].active &&
3973           !tape.player_participates[i])
3974       {
3975         struct PlayerInfo *player = &stored_player[i];
3976         int jx = player->jx, jy = player->jy;
3977
3978         player->active = FALSE;
3979         StorePlayer[jx][jy] = 0;
3980         Feld[jx][jy] = EL_EMPTY;
3981       }
3982     }
3983 #endif
3984   }
3985   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3986   {
3987     // when in single player mode, eliminate all but the local player
3988
3989     for (i = 0; i < MAX_PLAYERS; i++)
3990     {
3991       struct PlayerInfo *player = &stored_player[i];
3992
3993       if (player->active && player != local_player)
3994       {
3995         int jx = player->jx, jy = player->jy;
3996
3997         player->active = FALSE;
3998         player->present = FALSE;
3999
4000         StorePlayer[jx][jy] = 0;
4001         Feld[jx][jy] = EL_EMPTY;
4002       }
4003     }
4004   }
4005
4006   for (i = 0; i < MAX_PLAYERS; i++)
4007     if (stored_player[i].active)
4008       game.players_still_needed++;
4009
4010   if (level.solved_by_one_player)
4011     game.players_still_needed = 1;
4012
4013   // when recording the game, store which players take part in the game
4014   if (tape.recording)
4015   {
4016 #if USE_NEW_PLAYER_ASSIGNMENTS
4017     for (i = 0; i < MAX_PLAYERS; i++)
4018       if (stored_player[i].connected)
4019         tape.player_participates[i] = TRUE;
4020 #else
4021     for (i = 0; i < MAX_PLAYERS; i++)
4022       if (stored_player[i].active)
4023         tape.player_participates[i] = TRUE;
4024 #endif
4025   }
4026
4027 #if DEBUG_INIT_PLAYER
4028   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4029 #endif
4030
4031   if (BorderElement == EL_EMPTY)
4032   {
4033     SBX_Left = 0;
4034     SBX_Right = lev_fieldx - SCR_FIELDX;
4035     SBY_Upper = 0;
4036     SBY_Lower = lev_fieldy - SCR_FIELDY;
4037   }
4038   else
4039   {
4040     SBX_Left = -1;
4041     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4042     SBY_Upper = -1;
4043     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4044   }
4045
4046   if (full_lev_fieldx <= SCR_FIELDX)
4047     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4048   if (full_lev_fieldy <= SCR_FIELDY)
4049     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4050
4051   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4052     SBX_Left--;
4053   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4054     SBY_Upper--;
4055
4056   // if local player not found, look for custom element that might create
4057   // the player (make some assumptions about the right custom element)
4058   if (!local_player->present)
4059   {
4060     int start_x = 0, start_y = 0;
4061     int found_rating = 0;
4062     int found_element = EL_UNDEFINED;
4063     int player_nr = local_player->index_nr;
4064
4065     SCAN_PLAYFIELD(x, y)
4066     {
4067       int element = Feld[x][y];
4068       int content;
4069       int xx, yy;
4070       boolean is_player;
4071
4072       if (level.use_start_element[player_nr] &&
4073           level.start_element[player_nr] == element &&
4074           found_rating < 4)
4075       {
4076         start_x = x;
4077         start_y = y;
4078
4079         found_rating = 4;
4080         found_element = element;
4081       }
4082
4083       if (!IS_CUSTOM_ELEMENT(element))
4084         continue;
4085
4086       if (CAN_CHANGE(element))
4087       {
4088         for (i = 0; i < element_info[element].num_change_pages; i++)
4089         {
4090           // check for player created from custom element as single target
4091           content = element_info[element].change_page[i].target_element;
4092           is_player = ELEM_IS_PLAYER(content);
4093
4094           if (is_player && (found_rating < 3 ||
4095                             (found_rating == 3 && element < found_element)))
4096           {
4097             start_x = x;
4098             start_y = y;
4099
4100             found_rating = 3;
4101             found_element = element;
4102           }
4103         }
4104       }
4105
4106       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4107       {
4108         // check for player created from custom element as explosion content
4109         content = element_info[element].content.e[xx][yy];
4110         is_player = ELEM_IS_PLAYER(content);
4111
4112         if (is_player && (found_rating < 2 ||
4113                           (found_rating == 2 && element < found_element)))
4114         {
4115           start_x = x + xx - 1;
4116           start_y = y + yy - 1;
4117
4118           found_rating = 2;
4119           found_element = element;
4120         }
4121
4122         if (!CAN_CHANGE(element))
4123           continue;
4124
4125         for (i = 0; i < element_info[element].num_change_pages; i++)
4126         {
4127           // check for player created from custom element as extended target
4128           content =
4129             element_info[element].change_page[i].target_content.e[xx][yy];
4130
4131           is_player = ELEM_IS_PLAYER(content);
4132
4133           if (is_player && (found_rating < 1 ||
4134                             (found_rating == 1 && element < found_element)))
4135           {
4136             start_x = x + xx - 1;
4137             start_y = y + yy - 1;
4138
4139             found_rating = 1;
4140             found_element = element;
4141           }
4142         }
4143       }
4144     }
4145
4146     scroll_x = SCROLL_POSITION_X(start_x);
4147     scroll_y = SCROLL_POSITION_Y(start_y);
4148   }
4149   else
4150   {
4151     scroll_x = SCROLL_POSITION_X(local_player->jx);
4152     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4153   }
4154
4155   // !!! FIX THIS (START) !!!
4156   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4157   {
4158     InitGameEngine_EM();
4159   }
4160   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4161   {
4162     InitGameEngine_SP();
4163   }
4164   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4165   {
4166     InitGameEngine_MM();
4167   }
4168   else
4169   {
4170     DrawLevel(REDRAW_FIELD);
4171     DrawAllPlayers();
4172
4173     // after drawing the level, correct some elements
4174     if (game.timegate_time_left == 0)
4175       CloseAllOpenTimegates();
4176   }
4177
4178   // blit playfield from scroll buffer to normal back buffer for fading in
4179   BlitScreenToBitmap(backbuffer);
4180   // !!! FIX THIS (END) !!!
4181
4182   DrawMaskedBorder(fade_mask);
4183
4184   FadeIn(fade_mask);
4185
4186 #if 1
4187   // full screen redraw is required at this point in the following cases:
4188   // - special editor door undrawn when game was started from level editor
4189   // - drawing area (playfield) was changed and has to be removed completely
4190   redraw_mask = REDRAW_ALL;
4191   BackToFront();
4192 #endif
4193
4194   if (!game.restart_level)
4195   {
4196     // copy default game door content to main double buffer
4197
4198     // !!! CHECK AGAIN !!!
4199     SetPanelBackground();
4200     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4201     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4202   }
4203
4204   SetPanelBackground();
4205   SetDrawBackgroundMask(REDRAW_DOOR_1);
4206
4207   UpdateAndDisplayGameControlValues();
4208
4209   if (!game.restart_level)
4210   {
4211     UnmapGameButtons();
4212     UnmapTapeButtons();
4213
4214     FreeGameButtons();
4215     CreateGameButtons();
4216
4217     MapGameButtons();
4218     MapTapeButtons();
4219
4220     // copy actual game door content to door double buffer for OpenDoor()
4221     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4222
4223     OpenDoor(DOOR_OPEN_ALL);
4224
4225     KeyboardAutoRepeatOffUnlessAutoplay();
4226
4227 #if DEBUG_INIT_PLAYER
4228     DebugPrintPlayerStatus("Player status (final)");
4229 #endif
4230   }
4231
4232   UnmapAllGadgets();
4233
4234   MapGameButtons();
4235   MapTapeButtons();
4236
4237   if (!game.restart_level && !tape.playing)
4238   {
4239     LevelStats_incPlayed(level_nr);
4240
4241     SaveLevelSetup_SeriesInfo();
4242   }
4243
4244   game.restart_level = FALSE;
4245   game.restart_game_message = NULL;
4246   game.request_active = FALSE;
4247
4248   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4249     InitGameActions_MM();
4250
4251   SaveEngineSnapshotToListInitial();
4252
4253   if (!game.restart_level)
4254   {
4255     PlaySound(SND_GAME_STARTING);
4256
4257     if (setup.sound_music)
4258       PlayLevelMusic();
4259   }
4260 }
4261
4262 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4263                         int actual_player_x, int actual_player_y)
4264 {
4265   // this is used for non-R'n'D game engines to update certain engine values
4266
4267   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4268   {
4269     actual_player_x = correctLevelPosX_EM(actual_player_x);
4270     actual_player_y = correctLevelPosY_EM(actual_player_y);
4271   }
4272
4273   // needed to determine if sounds are played within the visible screen area
4274   scroll_x = actual_scroll_x;
4275   scroll_y = actual_scroll_y;
4276
4277   // needed to get player position for "follow finger" playing input method
4278   local_player->jx = actual_player_x;
4279   local_player->jy = actual_player_y;
4280 }
4281
4282 void InitMovDir(int x, int y)
4283 {
4284   int i, element = Feld[x][y];
4285   static int xy[4][2] =
4286   {
4287     {  0, +1 },
4288     { +1,  0 },
4289     {  0, -1 },
4290     { -1,  0 }
4291   };
4292   static int direction[3][4] =
4293   {
4294     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4295     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4296     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4297   };
4298
4299   switch (element)
4300   {
4301     case EL_BUG_RIGHT:
4302     case EL_BUG_UP:
4303     case EL_BUG_LEFT:
4304     case EL_BUG_DOWN:
4305       Feld[x][y] = EL_BUG;
4306       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4307       break;
4308
4309     case EL_SPACESHIP_RIGHT:
4310     case EL_SPACESHIP_UP:
4311     case EL_SPACESHIP_LEFT:
4312     case EL_SPACESHIP_DOWN:
4313       Feld[x][y] = EL_SPACESHIP;
4314       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4315       break;
4316
4317     case EL_BD_BUTTERFLY_RIGHT:
4318     case EL_BD_BUTTERFLY_UP:
4319     case EL_BD_BUTTERFLY_LEFT:
4320     case EL_BD_BUTTERFLY_DOWN:
4321       Feld[x][y] = EL_BD_BUTTERFLY;
4322       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4323       break;
4324
4325     case EL_BD_FIREFLY_RIGHT:
4326     case EL_BD_FIREFLY_UP:
4327     case EL_BD_FIREFLY_LEFT:
4328     case EL_BD_FIREFLY_DOWN:
4329       Feld[x][y] = EL_BD_FIREFLY;
4330       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4331       break;
4332
4333     case EL_PACMAN_RIGHT:
4334     case EL_PACMAN_UP:
4335     case EL_PACMAN_LEFT:
4336     case EL_PACMAN_DOWN:
4337       Feld[x][y] = EL_PACMAN;
4338       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4339       break;
4340
4341     case EL_YAMYAM_LEFT:
4342     case EL_YAMYAM_RIGHT:
4343     case EL_YAMYAM_UP:
4344     case EL_YAMYAM_DOWN:
4345       Feld[x][y] = EL_YAMYAM;
4346       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4347       break;
4348
4349     case EL_SP_SNIKSNAK:
4350       MovDir[x][y] = MV_UP;
4351       break;
4352
4353     case EL_SP_ELECTRON:
4354       MovDir[x][y] = MV_LEFT;
4355       break;
4356
4357     case EL_MOLE_LEFT:
4358     case EL_MOLE_RIGHT:
4359     case EL_MOLE_UP:
4360     case EL_MOLE_DOWN:
4361       Feld[x][y] = EL_MOLE;
4362       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4363       break;
4364
4365     default:
4366       if (IS_CUSTOM_ELEMENT(element))
4367       {
4368         struct ElementInfo *ei = &element_info[element];
4369         int move_direction_initial = ei->move_direction_initial;
4370         int move_pattern = ei->move_pattern;
4371
4372         if (move_direction_initial == MV_START_PREVIOUS)
4373         {
4374           if (MovDir[x][y] != MV_NONE)
4375             return;
4376
4377           move_direction_initial = MV_START_AUTOMATIC;
4378         }
4379
4380         if (move_direction_initial == MV_START_RANDOM)
4381           MovDir[x][y] = 1 << RND(4);
4382         else if (move_direction_initial & MV_ANY_DIRECTION)
4383           MovDir[x][y] = move_direction_initial;
4384         else if (move_pattern == MV_ALL_DIRECTIONS ||
4385                  move_pattern == MV_TURNING_LEFT ||
4386                  move_pattern == MV_TURNING_RIGHT ||
4387                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4388                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4389                  move_pattern == MV_TURNING_RANDOM)
4390           MovDir[x][y] = 1 << RND(4);
4391         else if (move_pattern == MV_HORIZONTAL)
4392           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4393         else if (move_pattern == MV_VERTICAL)
4394           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4395         else if (move_pattern & MV_ANY_DIRECTION)
4396           MovDir[x][y] = element_info[element].move_pattern;
4397         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4398                  move_pattern == MV_ALONG_RIGHT_SIDE)
4399         {
4400           // use random direction as default start direction
4401           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4402             MovDir[x][y] = 1 << RND(4);
4403
4404           for (i = 0; i < NUM_DIRECTIONS; i++)
4405           {
4406             int x1 = x + xy[i][0];
4407             int y1 = y + xy[i][1];
4408
4409             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4410             {
4411               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4412                 MovDir[x][y] = direction[0][i];
4413               else
4414                 MovDir[x][y] = direction[1][i];
4415
4416               break;
4417             }
4418           }
4419         }                
4420       }
4421       else
4422       {
4423         MovDir[x][y] = 1 << RND(4);
4424
4425         if (element != EL_BUG &&
4426             element != EL_SPACESHIP &&
4427             element != EL_BD_BUTTERFLY &&
4428             element != EL_BD_FIREFLY)
4429           break;
4430
4431         for (i = 0; i < NUM_DIRECTIONS; i++)
4432         {
4433           int x1 = x + xy[i][0];
4434           int y1 = y + xy[i][1];
4435
4436           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4437           {
4438             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4439             {
4440               MovDir[x][y] = direction[0][i];
4441               break;
4442             }
4443             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4444                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4445             {
4446               MovDir[x][y] = direction[1][i];
4447               break;
4448             }
4449           }
4450         }
4451       }
4452       break;
4453   }
4454
4455   GfxDir[x][y] = MovDir[x][y];
4456 }
4457
4458 void InitAmoebaNr(int x, int y)
4459 {
4460   int i;
4461   int group_nr = AmoebeNachbarNr(x, y);
4462
4463   if (group_nr == 0)
4464   {
4465     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4466     {
4467       if (AmoebaCnt[i] == 0)
4468       {
4469         group_nr = i;
4470         break;
4471       }
4472     }
4473   }
4474
4475   AmoebaNr[x][y] = group_nr;
4476   AmoebaCnt[group_nr]++;
4477   AmoebaCnt2[group_nr]++;
4478 }
4479
4480 static void LevelSolved(void)
4481 {
4482   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4483       game.players_still_needed > 0)
4484     return;
4485
4486   game.LevelSolved = TRUE;
4487   game.GameOver = TRUE;
4488
4489   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4490                       level.native_em_level->lev->score :
4491                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4492                       game_mm.score :
4493                       game.score);
4494   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4495                        MM_HEALTH(game_mm.laser_overload_value) :
4496                        game.health);
4497
4498   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4499   game.LevelSolved_CountingScore = game.score_final;
4500   game.LevelSolved_CountingHealth = game.health_final;
4501 }
4502
4503 void GameWon(void)
4504 {
4505   static int time_count_steps;
4506   static int time, time_final;
4507   static int score, score_final;
4508   static int health, health_final;
4509   static int game_over_delay_1 = 0;
4510   static int game_over_delay_2 = 0;
4511   static int game_over_delay_3 = 0;
4512   int game_over_delay_value_1 = 50;
4513   int game_over_delay_value_2 = 25;
4514   int game_over_delay_value_3 = 50;
4515
4516   if (!game.LevelSolved_GameWon)
4517   {
4518     int i;
4519
4520     // do not start end game actions before the player stops moving (to exit)
4521     if (local_player->active && local_player->MovPos)
4522       return;
4523
4524     game.LevelSolved_GameWon = TRUE;
4525     game.LevelSolved_SaveTape = tape.recording;
4526     game.LevelSolved_SaveScore = !tape.playing;
4527
4528     if (!tape.playing)
4529     {
4530       LevelStats_incSolved(level_nr);
4531
4532       SaveLevelSetup_SeriesInfo();
4533     }
4534
4535     if (tape.auto_play)         // tape might already be stopped here
4536       tape.auto_play_level_solved = TRUE;
4537
4538     TapeStop();
4539
4540     game_over_delay_1 = 0;
4541     game_over_delay_2 = 0;
4542     game_over_delay_3 = game_over_delay_value_3;
4543
4544     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4545     score = score_final = game.score_final;
4546     health = health_final = game.health_final;
4547
4548     if (level.score[SC_TIME_BONUS] > 0)
4549     {
4550       if (TimeLeft > 0)
4551       {
4552         time_final = 0;
4553         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4554       }
4555       else if (game.no_time_limit && TimePlayed < 999)
4556       {
4557         time_final = 999;
4558         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4559       }
4560
4561       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4562
4563       game_over_delay_1 = game_over_delay_value_1;
4564
4565       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4566       {
4567         health_final = 0;
4568         score_final += health * level.score[SC_TIME_BONUS];
4569
4570         game_over_delay_2 = game_over_delay_value_2;
4571       }
4572
4573       game.score_final = score_final;
4574       game.health_final = health_final;
4575     }
4576
4577     if (level_editor_test_game)
4578     {
4579       time = time_final;
4580       score = score_final;
4581
4582       game.LevelSolved_CountingTime = time;
4583       game.LevelSolved_CountingScore = score;
4584
4585       game_panel_controls[GAME_PANEL_TIME].value = time;
4586       game_panel_controls[GAME_PANEL_SCORE].value = score;
4587
4588       DisplayGameControlValues();
4589     }
4590
4591     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4592     {
4593       // check if last player has left the level
4594       if (game.exit_x >= 0 &&
4595           game.exit_y >= 0)
4596       {
4597         int x = game.exit_x;
4598         int y = game.exit_y;
4599         int element = Feld[x][y];
4600
4601         // close exit door after last player
4602         if ((game.all_players_gone &&
4603              (element == EL_EXIT_OPEN ||
4604               element == EL_SP_EXIT_OPEN ||
4605               element == EL_STEEL_EXIT_OPEN)) ||
4606             element == EL_EM_EXIT_OPEN ||
4607             element == EL_EM_STEEL_EXIT_OPEN)
4608         {
4609
4610           Feld[x][y] =
4611             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4612              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4613              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4614              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4615              EL_EM_STEEL_EXIT_CLOSING);
4616
4617           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4618         }
4619
4620         // player disappears
4621         DrawLevelField(x, y);
4622       }
4623
4624       for (i = 0; i < MAX_PLAYERS; i++)
4625       {
4626         struct PlayerInfo *player = &stored_player[i];
4627
4628         if (player->present)
4629         {
4630           RemovePlayer(player);
4631
4632           // player disappears
4633           DrawLevelField(player->jx, player->jy);
4634         }
4635       }
4636     }
4637
4638     PlaySound(SND_GAME_WINNING);
4639   }
4640
4641   if (game_over_delay_1 > 0)
4642   {
4643     game_over_delay_1--;
4644
4645     return;
4646   }
4647
4648   if (time != time_final)
4649   {
4650     int time_to_go = ABS(time_final - time);
4651     int time_count_dir = (time < time_final ? +1 : -1);
4652
4653     if (time_to_go < time_count_steps)
4654       time_count_steps = 1;
4655
4656     time  += time_count_steps * time_count_dir;
4657     score += time_count_steps * level.score[SC_TIME_BONUS];
4658
4659     game.LevelSolved_CountingTime = time;
4660     game.LevelSolved_CountingScore = score;
4661
4662     game_panel_controls[GAME_PANEL_TIME].value = time;
4663     game_panel_controls[GAME_PANEL_SCORE].value = score;
4664
4665     DisplayGameControlValues();
4666
4667     if (time == time_final)
4668       StopSound(SND_GAME_LEVELTIME_BONUS);
4669     else if (setup.sound_loops)
4670       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4671     else
4672       PlaySound(SND_GAME_LEVELTIME_BONUS);
4673
4674     return;
4675   }
4676
4677   if (game_over_delay_2 > 0)
4678   {
4679     game_over_delay_2--;
4680
4681     return;
4682   }
4683
4684   if (health != health_final)
4685   {
4686     int health_count_dir = (health < health_final ? +1 : -1);
4687
4688     health += health_count_dir;
4689     score  += level.score[SC_TIME_BONUS];
4690
4691     game.LevelSolved_CountingHealth = health;
4692     game.LevelSolved_CountingScore = score;
4693
4694     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4695     game_panel_controls[GAME_PANEL_SCORE].value = score;
4696
4697     DisplayGameControlValues();
4698
4699     if (health == health_final)
4700       StopSound(SND_GAME_LEVELTIME_BONUS);
4701     else if (setup.sound_loops)
4702       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4703     else
4704       PlaySound(SND_GAME_LEVELTIME_BONUS);
4705
4706     return;
4707   }
4708
4709   game.panel.active = FALSE;
4710
4711   if (game_over_delay_3 > 0)
4712   {
4713     game_over_delay_3--;
4714
4715     return;
4716   }
4717
4718   GameEnd();
4719 }
4720
4721 void GameEnd(void)
4722 {
4723   // used instead of "level_nr" (needed for network games)
4724   int last_level_nr = levelset.level_nr;
4725   int hi_pos;
4726
4727   game.LevelSolved_GameEnd = TRUE;
4728
4729   if (game.LevelSolved_SaveTape)
4730   {
4731     // make sure that request dialog to save tape does not open door again
4732     if (!global.use_envelope_request)
4733       CloseDoor(DOOR_CLOSE_1);
4734
4735     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4736   }
4737
4738   // if no tape is to be saved, close both doors simultaneously
4739   CloseDoor(DOOR_CLOSE_ALL);
4740
4741   if (level_editor_test_game)
4742   {
4743     SetGameStatus(GAME_MODE_MAIN);
4744
4745     DrawMainMenu();
4746
4747     return;
4748   }
4749
4750   if (!game.LevelSolved_SaveScore)
4751   {
4752     SetGameStatus(GAME_MODE_MAIN);
4753
4754     DrawMainMenu();
4755
4756     return;
4757   }
4758
4759   if (level_nr == leveldir_current->handicap_level)
4760   {
4761     leveldir_current->handicap_level++;
4762
4763     SaveLevelSetup_SeriesInfo();
4764   }
4765
4766   if (setup.increment_levels &&
4767       level_nr < leveldir_current->last_level &&
4768       !network_playing)
4769   {
4770     level_nr++;         // advance to next level
4771     TapeErase();        // start with empty tape
4772
4773     if (setup.auto_play_next_level)
4774     {
4775       LoadLevel(level_nr);
4776
4777       SaveLevelSetup_SeriesInfo();
4778     }
4779   }
4780
4781   hi_pos = NewHiScore(last_level_nr);
4782
4783   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4784   {
4785     SetGameStatus(GAME_MODE_SCORES);
4786
4787     DrawHallOfFame(last_level_nr, hi_pos);
4788   }
4789   else if (setup.auto_play_next_level && setup.increment_levels &&
4790            last_level_nr < leveldir_current->last_level &&
4791            !network_playing)
4792   {
4793     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4794   }
4795   else
4796   {
4797     SetGameStatus(GAME_MODE_MAIN);
4798
4799     DrawMainMenu();
4800   }
4801 }
4802
4803 int NewHiScore(int level_nr)
4804 {
4805   int k, l;
4806   int position = -1;
4807   boolean one_score_entry_per_name = !program.many_scores_per_name;
4808
4809   LoadScore(level_nr);
4810
4811   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4812       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4813     return -1;
4814
4815   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4816   {
4817     if (game.score_final > highscore[k].Score)
4818     {
4819       // player has made it to the hall of fame
4820
4821       if (k < MAX_SCORE_ENTRIES - 1)
4822       {
4823         int m = MAX_SCORE_ENTRIES - 1;
4824
4825         if (one_score_entry_per_name)
4826         {
4827           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4828             if (strEqual(setup.player_name, highscore[l].Name))
4829               m = l;
4830
4831           if (m == k)   // player's new highscore overwrites his old one
4832             goto put_into_list;
4833         }
4834
4835         for (l = m; l > k; l--)
4836         {
4837           strcpy(highscore[l].Name, highscore[l - 1].Name);
4838           highscore[l].Score = highscore[l - 1].Score;
4839         }
4840       }
4841
4842       put_into_list:
4843
4844       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4845       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4846       highscore[k].Score = game.score_final;
4847       position = k;
4848
4849       break;
4850     }
4851     else if (one_score_entry_per_name &&
4852              !strncmp(setup.player_name, highscore[k].Name,
4853                       MAX_PLAYER_NAME_LEN))
4854       break;    // player already there with a higher score
4855   }
4856
4857   if (position >= 0) 
4858     SaveScore(level_nr);
4859
4860   return position;
4861 }
4862
4863 static int getElementMoveStepsizeExt(int x, int y, int direction)
4864 {
4865   int element = Feld[x][y];
4866   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4867   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4868   int horiz_move = (dx != 0);
4869   int sign = (horiz_move ? dx : dy);
4870   int step = sign * element_info[element].move_stepsize;
4871
4872   // special values for move stepsize for spring and things on conveyor belt
4873   if (horiz_move)
4874   {
4875     if (CAN_FALL(element) &&
4876         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4877       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4878     else if (element == EL_SPRING)
4879       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4880   }
4881
4882   return step;
4883 }
4884
4885 static int getElementMoveStepsize(int x, int y)
4886 {
4887   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4888 }
4889
4890 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4891 {
4892   if (player->GfxAction != action || player->GfxDir != dir)
4893   {
4894     player->GfxAction = action;
4895     player->GfxDir = dir;
4896     player->Frame = 0;
4897     player->StepFrame = 0;
4898   }
4899 }
4900
4901 static void ResetGfxFrame(int x, int y)
4902 {
4903   // profiling showed that "autotest" spends 10~20% of its time in this function
4904   if (DrawingDeactivatedField())
4905     return;
4906
4907   int element = Feld[x][y];
4908   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4909
4910   if (graphic_info[graphic].anim_global_sync)
4911     GfxFrame[x][y] = FrameCounter;
4912   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4913     GfxFrame[x][y] = CustomValue[x][y];
4914   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4915     GfxFrame[x][y] = element_info[element].collect_score;
4916   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4917     GfxFrame[x][y] = ChangeDelay[x][y];
4918 }
4919
4920 static void ResetGfxAnimation(int x, int y)
4921 {
4922   GfxAction[x][y] = ACTION_DEFAULT;
4923   GfxDir[x][y] = MovDir[x][y];
4924   GfxFrame[x][y] = 0;
4925
4926   ResetGfxFrame(x, y);
4927 }
4928
4929 static void ResetRandomAnimationValue(int x, int y)
4930 {
4931   GfxRandom[x][y] = INIT_GFX_RANDOM();
4932 }
4933
4934 static void InitMovingField(int x, int y, int direction)
4935 {
4936   int element = Feld[x][y];
4937   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4938   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4939   int newx = x + dx;
4940   int newy = y + dy;
4941   boolean is_moving_before, is_moving_after;
4942
4943   // check if element was/is moving or being moved before/after mode change
4944   is_moving_before = (WasJustMoving[x][y] != 0);
4945   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4946
4947   // reset animation only for moving elements which change direction of moving
4948   // or which just started or stopped moving
4949   // (else CEs with property "can move" / "not moving" are reset each frame)
4950   if (is_moving_before != is_moving_after ||
4951       direction != MovDir[x][y])
4952     ResetGfxAnimation(x, y);
4953
4954   MovDir[x][y] = direction;
4955   GfxDir[x][y] = direction;
4956
4957   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4958                      direction == MV_DOWN && CAN_FALL(element) ?
4959                      ACTION_FALLING : ACTION_MOVING);
4960
4961   // this is needed for CEs with property "can move" / "not moving"
4962
4963   if (is_moving_after)
4964   {
4965     if (Feld[newx][newy] == EL_EMPTY)
4966       Feld[newx][newy] = EL_BLOCKED;
4967
4968     MovDir[newx][newy] = MovDir[x][y];
4969
4970     CustomValue[newx][newy] = CustomValue[x][y];
4971
4972     GfxFrame[newx][newy] = GfxFrame[x][y];
4973     GfxRandom[newx][newy] = GfxRandom[x][y];
4974     GfxAction[newx][newy] = GfxAction[x][y];
4975     GfxDir[newx][newy] = GfxDir[x][y];
4976   }
4977 }
4978
4979 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4980 {
4981   int direction = MovDir[x][y];
4982   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4983   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4984
4985   *goes_to_x = newx;
4986   *goes_to_y = newy;
4987 }
4988
4989 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4990 {
4991   int oldx = x, oldy = y;
4992   int direction = MovDir[x][y];
4993
4994   if (direction == MV_LEFT)
4995     oldx++;
4996   else if (direction == MV_RIGHT)
4997     oldx--;
4998   else if (direction == MV_UP)
4999     oldy++;
5000   else if (direction == MV_DOWN)
5001     oldy--;
5002
5003   *comes_from_x = oldx;
5004   *comes_from_y = oldy;
5005 }
5006
5007 static int MovingOrBlocked2Element(int x, int y)
5008 {
5009   int element = Feld[x][y];
5010
5011   if (element == EL_BLOCKED)
5012   {
5013     int oldx, oldy;
5014
5015     Blocked2Moving(x, y, &oldx, &oldy);
5016     return Feld[oldx][oldy];
5017   }
5018   else
5019     return element;
5020 }
5021
5022 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5023 {
5024   // like MovingOrBlocked2Element(), but if element is moving
5025   // and (x,y) is the field the moving element is just leaving,
5026   // return EL_BLOCKED instead of the element value
5027   int element = Feld[x][y];
5028
5029   if (IS_MOVING(x, y))
5030   {
5031     if (element == EL_BLOCKED)
5032     {
5033       int oldx, oldy;
5034
5035       Blocked2Moving(x, y, &oldx, &oldy);
5036       return Feld[oldx][oldy];
5037     }
5038     else
5039       return EL_BLOCKED;
5040   }
5041   else
5042     return element;
5043 }
5044
5045 static void RemoveField(int x, int y)
5046 {
5047   Feld[x][y] = EL_EMPTY;
5048
5049   MovPos[x][y] = 0;
5050   MovDir[x][y] = 0;
5051   MovDelay[x][y] = 0;
5052
5053   CustomValue[x][y] = 0;
5054
5055   AmoebaNr[x][y] = 0;
5056   ChangeDelay[x][y] = 0;
5057   ChangePage[x][y] = -1;
5058   Pushed[x][y] = FALSE;
5059
5060   GfxElement[x][y] = EL_UNDEFINED;
5061   GfxAction[x][y] = ACTION_DEFAULT;
5062   GfxDir[x][y] = MV_NONE;
5063 }
5064
5065 static void RemoveMovingField(int x, int y)
5066 {
5067   int oldx = x, oldy = y, newx = x, newy = y;
5068   int element = Feld[x][y];
5069   int next_element = EL_UNDEFINED;
5070
5071   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5072     return;
5073
5074   if (IS_MOVING(x, y))
5075   {
5076     Moving2Blocked(x, y, &newx, &newy);
5077
5078     if (Feld[newx][newy] != EL_BLOCKED)
5079     {
5080       // element is moving, but target field is not free (blocked), but
5081       // already occupied by something different (example: acid pool);
5082       // in this case, only remove the moving field, but not the target
5083
5084       RemoveField(oldx, oldy);
5085
5086       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5087
5088       TEST_DrawLevelField(oldx, oldy);
5089
5090       return;
5091     }
5092   }
5093   else if (element == EL_BLOCKED)
5094   {
5095     Blocked2Moving(x, y, &oldx, &oldy);
5096     if (!IS_MOVING(oldx, oldy))
5097       return;
5098   }
5099
5100   if (element == EL_BLOCKED &&
5101       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5102        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5103        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5104        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5105        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5106        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5107     next_element = get_next_element(Feld[oldx][oldy]);
5108
5109   RemoveField(oldx, oldy);
5110   RemoveField(newx, newy);
5111
5112   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5113
5114   if (next_element != EL_UNDEFINED)
5115     Feld[oldx][oldy] = next_element;
5116
5117   TEST_DrawLevelField(oldx, oldy);
5118   TEST_DrawLevelField(newx, newy);
5119 }
5120
5121 void DrawDynamite(int x, int y)
5122 {
5123   int sx = SCREENX(x), sy = SCREENY(y);
5124   int graphic = el2img(Feld[x][y]);
5125   int frame;
5126
5127   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5128     return;
5129
5130   if (IS_WALKABLE_INSIDE(Back[x][y]))
5131     return;
5132
5133   if (Back[x][y])
5134     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5135   else if (Store[x][y])
5136     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5137
5138   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5139
5140   if (Back[x][y] || Store[x][y])
5141     DrawGraphicThruMask(sx, sy, graphic, frame);
5142   else
5143     DrawGraphic(sx, sy, graphic, frame);
5144 }
5145
5146 static void CheckDynamite(int x, int y)
5147 {
5148   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5149   {
5150     MovDelay[x][y]--;
5151
5152     if (MovDelay[x][y] != 0)
5153     {
5154       DrawDynamite(x, y);
5155       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5156
5157       return;
5158     }
5159   }
5160
5161   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5162
5163   Bang(x, y);
5164 }
5165
5166 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5167 {
5168   boolean num_checked_players = 0;
5169   int i;
5170
5171   for (i = 0; i < MAX_PLAYERS; i++)
5172   {
5173     if (stored_player[i].active)
5174     {
5175       int sx = stored_player[i].jx;
5176       int sy = stored_player[i].jy;
5177
5178       if (num_checked_players == 0)
5179       {
5180         *sx1 = *sx2 = sx;
5181         *sy1 = *sy2 = sy;
5182       }
5183       else
5184       {
5185         *sx1 = MIN(*sx1, sx);
5186         *sy1 = MIN(*sy1, sy);
5187         *sx2 = MAX(*sx2, sx);
5188         *sy2 = MAX(*sy2, sy);
5189       }
5190
5191       num_checked_players++;
5192     }
5193   }
5194 }
5195
5196 static boolean checkIfAllPlayersFitToScreen_RND(void)
5197 {
5198   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5199
5200   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5201
5202   return (sx2 - sx1 < SCR_FIELDX &&
5203           sy2 - sy1 < SCR_FIELDY);
5204 }
5205
5206 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5207 {
5208   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5209
5210   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5211
5212   *sx = (sx1 + sx2) / 2;
5213   *sy = (sy1 + sy2) / 2;
5214 }
5215
5216 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5217                                boolean center_screen, boolean quick_relocation)
5218 {
5219   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5220   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5221   boolean no_delay = (tape.warp_forward);
5222   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5223   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5224   int new_scroll_x, new_scroll_y;
5225
5226   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5227   {
5228     // case 1: quick relocation inside visible screen (without scrolling)
5229
5230     RedrawPlayfield();
5231
5232     return;
5233   }
5234
5235   if (!level.shifted_relocation || center_screen)
5236   {
5237     // relocation _with_ centering of screen
5238
5239     new_scroll_x = SCROLL_POSITION_X(x);
5240     new_scroll_y = SCROLL_POSITION_Y(y);
5241   }
5242   else
5243   {
5244     // relocation _without_ centering of screen
5245
5246     int center_scroll_x = SCROLL_POSITION_X(old_x);
5247     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5248     int offset_x = x + (scroll_x - center_scroll_x);
5249     int offset_y = y + (scroll_y - center_scroll_y);
5250
5251     // for new screen position, apply previous offset to center position
5252     new_scroll_x = SCROLL_POSITION_X(offset_x);
5253     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5254   }
5255
5256   if (quick_relocation)
5257   {
5258     // case 2: quick relocation (redraw without visible scrolling)
5259
5260     scroll_x = new_scroll_x;
5261     scroll_y = new_scroll_y;
5262
5263     RedrawPlayfield();
5264
5265     return;
5266   }
5267
5268   // case 3: visible relocation (with scrolling to new position)
5269
5270   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5271
5272   SetVideoFrameDelay(wait_delay_value);
5273
5274   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5275   {
5276     int dx = 0, dy = 0;
5277     int fx = FX, fy = FY;
5278
5279     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5280     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5281
5282     if (dx == 0 && dy == 0)             // no scrolling needed at all
5283       break;
5284
5285     scroll_x -= dx;
5286     scroll_y -= dy;
5287
5288     fx += dx * TILEX / 2;
5289     fy += dy * TILEY / 2;
5290
5291     ScrollLevel(dx, dy);
5292     DrawAllPlayers();
5293
5294     // scroll in two steps of half tile size to make things smoother
5295     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5296
5297     // scroll second step to align at full tile size
5298     BlitScreenToBitmap(window);
5299   }
5300
5301   DrawAllPlayers();
5302   BackToFront();
5303
5304   SetVideoFrameDelay(frame_delay_value_old);
5305 }
5306
5307 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5308 {
5309   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5310   int player_nr = GET_PLAYER_NR(el_player);
5311   struct PlayerInfo *player = &stored_player[player_nr];
5312   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5313   boolean no_delay = (tape.warp_forward);
5314   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5315   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5316   int old_jx = player->jx;
5317   int old_jy = player->jy;
5318   int old_element = Feld[old_jx][old_jy];
5319   int element = Feld[jx][jy];
5320   boolean player_relocated = (old_jx != jx || old_jy != jy);
5321
5322   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5323   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5324   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5325   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5326   int leave_side_horiz = move_dir_horiz;
5327   int leave_side_vert  = move_dir_vert;
5328   int enter_side = enter_side_horiz | enter_side_vert;
5329   int leave_side = leave_side_horiz | leave_side_vert;
5330
5331   if (player->buried)           // do not reanimate dead player
5332     return;
5333
5334   if (!player_relocated)        // no need to relocate the player
5335     return;
5336
5337   if (IS_PLAYER(jx, jy))        // player already placed at new position
5338   {
5339     RemoveField(jx, jy);        // temporarily remove newly placed player
5340     DrawLevelField(jx, jy);
5341   }
5342
5343   if (player->present)
5344   {
5345     while (player->MovPos)
5346     {
5347       ScrollPlayer(player, SCROLL_GO_ON);
5348       ScrollScreen(NULL, SCROLL_GO_ON);
5349
5350       AdvanceFrameAndPlayerCounters(player->index_nr);
5351
5352       DrawPlayer(player);
5353
5354       BackToFront_WithFrameDelay(wait_delay_value);
5355     }
5356
5357     DrawPlayer(player);         // needed here only to cleanup last field
5358     DrawLevelField(player->jx, player->jy);     // remove player graphic
5359
5360     player->is_moving = FALSE;
5361   }
5362
5363   if (IS_CUSTOM_ELEMENT(old_element))
5364     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5365                                CE_LEFT_BY_PLAYER,
5366                                player->index_bit, leave_side);
5367
5368   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5369                                       CE_PLAYER_LEAVES_X,
5370                                       player->index_bit, leave_side);
5371
5372   Feld[jx][jy] = el_player;
5373   InitPlayerField(jx, jy, el_player, TRUE);
5374
5375   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5376      possible that the relocation target field did not contain a player element,
5377      but a walkable element, to which the new player was relocated -- in this
5378      case, restore that (already initialized!) element on the player field */
5379   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5380   {
5381     Feld[jx][jy] = element;     // restore previously existing element
5382   }
5383
5384   // only visually relocate centered player
5385   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5386                      FALSE, level.instant_relocation);
5387
5388   TestIfPlayerTouchesBadThing(jx, jy);
5389   TestIfPlayerTouchesCustomElement(jx, jy);
5390
5391   if (IS_CUSTOM_ELEMENT(element))
5392     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5393                                player->index_bit, enter_side);
5394
5395   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5396                                       player->index_bit, enter_side);
5397
5398   if (player->is_switching)
5399   {
5400     /* ensure that relocation while still switching an element does not cause
5401        a new element to be treated as also switched directly after relocation
5402        (this is important for teleporter switches that teleport the player to
5403        a place where another teleporter switch is in the same direction, which
5404        would then incorrectly be treated as immediately switched before the
5405        direction key that caused the switch was released) */
5406
5407     player->switch_x += jx - old_jx;
5408     player->switch_y += jy - old_jy;
5409   }
5410 }
5411
5412 static void Explode(int ex, int ey, int phase, int mode)
5413 {
5414   int x, y;
5415   int last_phase;
5416   int border_element;
5417
5418   // !!! eliminate this variable !!!
5419   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5420
5421   if (game.explosions_delayed)
5422   {
5423     ExplodeField[ex][ey] = mode;
5424     return;
5425   }
5426
5427   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5428   {
5429     int center_element = Feld[ex][ey];
5430     int artwork_element, explosion_element;     // set these values later
5431
5432     // remove things displayed in background while burning dynamite
5433     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5434       Back[ex][ey] = 0;
5435
5436     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5437     {
5438       // put moving element to center field (and let it explode there)
5439       center_element = MovingOrBlocked2Element(ex, ey);
5440       RemoveMovingField(ex, ey);
5441       Feld[ex][ey] = center_element;
5442     }
5443
5444     // now "center_element" is finally determined -- set related values now
5445     artwork_element = center_element;           // for custom player artwork
5446     explosion_element = center_element;         // for custom player artwork
5447
5448     if (IS_PLAYER(ex, ey))
5449     {
5450       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5451
5452       artwork_element = stored_player[player_nr].artwork_element;
5453
5454       if (level.use_explosion_element[player_nr])
5455       {
5456         explosion_element = level.explosion_element[player_nr];
5457         artwork_element = explosion_element;
5458       }
5459     }
5460
5461     if (mode == EX_TYPE_NORMAL ||
5462         mode == EX_TYPE_CENTER ||
5463         mode == EX_TYPE_CROSS)
5464       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5465
5466     last_phase = element_info[explosion_element].explosion_delay + 1;
5467
5468     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5469     {
5470       int xx = x - ex + 1;
5471       int yy = y - ey + 1;
5472       int element;
5473
5474       if (!IN_LEV_FIELD(x, y) ||
5475           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5476           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5477         continue;
5478
5479       element = Feld[x][y];
5480
5481       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5482       {
5483         element = MovingOrBlocked2Element(x, y);
5484
5485         if (!IS_EXPLOSION_PROOF(element))
5486           RemoveMovingField(x, y);
5487       }
5488
5489       // indestructible elements can only explode in center (but not flames)
5490       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5491                                            mode == EX_TYPE_BORDER)) ||
5492           element == EL_FLAMES)
5493         continue;
5494
5495       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5496          behaviour, for example when touching a yamyam that explodes to rocks
5497          with active deadly shield, a rock is created under the player !!! */
5498       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5499 #if 0
5500       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5501           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5502            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5503 #else
5504       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5505 #endif
5506       {
5507         if (IS_ACTIVE_BOMB(element))
5508         {
5509           // re-activate things under the bomb like gate or penguin
5510           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5511           Back[x][y] = 0;
5512         }
5513
5514         continue;
5515       }
5516
5517       // save walkable background elements while explosion on same tile
5518       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5519           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5520         Back[x][y] = element;
5521
5522       // ignite explodable elements reached by other explosion
5523       if (element == EL_EXPLOSION)
5524         element = Store2[x][y];
5525
5526       if (AmoebaNr[x][y] &&
5527           (element == EL_AMOEBA_FULL ||
5528            element == EL_BD_AMOEBA ||
5529            element == EL_AMOEBA_GROWING))
5530       {
5531         AmoebaCnt[AmoebaNr[x][y]]--;
5532         AmoebaCnt2[AmoebaNr[x][y]]--;
5533       }
5534
5535       RemoveField(x, y);
5536
5537       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5538       {
5539         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5540
5541         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5542
5543         if (PLAYERINFO(ex, ey)->use_murphy)
5544           Store[x][y] = EL_EMPTY;
5545       }
5546
5547       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5548       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5549       else if (ELEM_IS_PLAYER(center_element))
5550         Store[x][y] = EL_EMPTY;
5551       else if (center_element == EL_YAMYAM)
5552         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5553       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5554         Store[x][y] = element_info[center_element].content.e[xx][yy];
5555 #if 1
5556       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5557       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5558       // otherwise) -- FIX THIS !!!
5559       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5560         Store[x][y] = element_info[element].content.e[1][1];
5561 #else
5562       else if (!CAN_EXPLODE(element))
5563         Store[x][y] = element_info[element].content.e[1][1];
5564 #endif
5565       else
5566         Store[x][y] = EL_EMPTY;
5567
5568       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5569           center_element == EL_AMOEBA_TO_DIAMOND)
5570         Store2[x][y] = element;
5571
5572       Feld[x][y] = EL_EXPLOSION;
5573       GfxElement[x][y] = artwork_element;
5574
5575       ExplodePhase[x][y] = 1;
5576       ExplodeDelay[x][y] = last_phase;
5577
5578       Stop[x][y] = TRUE;
5579     }
5580
5581     if (center_element == EL_YAMYAM)
5582       game.yamyam_content_nr =
5583         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5584
5585     return;
5586   }
5587
5588   if (Stop[ex][ey])
5589     return;
5590
5591   x = ex;
5592   y = ey;
5593
5594   if (phase == 1)
5595     GfxFrame[x][y] = 0;         // restart explosion animation
5596
5597   last_phase = ExplodeDelay[x][y];
5598
5599   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5600
5601   // this can happen if the player leaves an explosion just in time
5602   if (GfxElement[x][y] == EL_UNDEFINED)
5603     GfxElement[x][y] = EL_EMPTY;
5604
5605   border_element = Store2[x][y];
5606   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5607     border_element = StorePlayer[x][y];
5608
5609   if (phase == element_info[border_element].ignition_delay ||
5610       phase == last_phase)
5611   {
5612     boolean border_explosion = FALSE;
5613
5614     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5615         !PLAYER_EXPLOSION_PROTECTED(x, y))
5616     {
5617       KillPlayerUnlessExplosionProtected(x, y);
5618       border_explosion = TRUE;
5619     }
5620     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5621     {
5622       Feld[x][y] = Store2[x][y];
5623       Store2[x][y] = 0;
5624       Bang(x, y);
5625       border_explosion = TRUE;
5626     }
5627     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5628     {
5629       AmoebeUmwandeln(x, y);
5630       Store2[x][y] = 0;
5631       border_explosion = TRUE;
5632     }
5633
5634     // if an element just explodes due to another explosion (chain-reaction),
5635     // do not immediately end the new explosion when it was the last frame of
5636     // the explosion (as it would be done in the following "if"-statement!)
5637     if (border_explosion && phase == last_phase)
5638       return;
5639   }
5640
5641   if (phase == last_phase)
5642   {
5643     int element;
5644
5645     element = Feld[x][y] = Store[x][y];
5646     Store[x][y] = Store2[x][y] = 0;
5647     GfxElement[x][y] = EL_UNDEFINED;
5648
5649     // player can escape from explosions and might therefore be still alive
5650     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5651         element <= EL_PLAYER_IS_EXPLODING_4)
5652     {
5653       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5654       int explosion_element = EL_PLAYER_1 + player_nr;
5655       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5656       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5657
5658       if (level.use_explosion_element[player_nr])
5659         explosion_element = level.explosion_element[player_nr];
5660
5661       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5662                     element_info[explosion_element].content.e[xx][yy]);
5663     }
5664
5665     // restore probably existing indestructible background element
5666     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5667       element = Feld[x][y] = Back[x][y];
5668     Back[x][y] = 0;
5669
5670     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5671     GfxDir[x][y] = MV_NONE;
5672     ChangeDelay[x][y] = 0;
5673     ChangePage[x][y] = -1;
5674
5675     CustomValue[x][y] = 0;
5676
5677     InitField_WithBug2(x, y, FALSE);
5678
5679     TEST_DrawLevelField(x, y);
5680
5681     TestIfElementTouchesCustomElement(x, y);
5682
5683     if (GFX_CRUMBLED(element))
5684       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5685
5686     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5687       StorePlayer[x][y] = 0;
5688
5689     if (ELEM_IS_PLAYER(element))
5690       RelocatePlayer(x, y, element);
5691   }
5692   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5693   {
5694     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5695     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5696
5697     if (phase == delay)
5698       TEST_DrawLevelFieldCrumbled(x, y);
5699
5700     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5701     {
5702       DrawLevelElement(x, y, Back[x][y]);
5703       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5704     }
5705     else if (IS_WALKABLE_UNDER(Back[x][y]))
5706     {
5707       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5708       DrawLevelElementThruMask(x, y, Back[x][y]);
5709     }
5710     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5711       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5712   }
5713 }
5714
5715 static void DynaExplode(int ex, int ey)
5716 {
5717   int i, j;
5718   int dynabomb_element = Feld[ex][ey];
5719   int dynabomb_size = 1;
5720   boolean dynabomb_xl = FALSE;
5721   struct PlayerInfo *player;
5722   static int xy[4][2] =
5723   {
5724     { 0, -1 },
5725     { -1, 0 },
5726     { +1, 0 },
5727     { 0, +1 }
5728   };
5729
5730   if (IS_ACTIVE_BOMB(dynabomb_element))
5731   {
5732     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5733     dynabomb_size = player->dynabomb_size;
5734     dynabomb_xl = player->dynabomb_xl;
5735     player->dynabombs_left++;
5736   }
5737
5738   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5739
5740   for (i = 0; i < NUM_DIRECTIONS; i++)
5741   {
5742     for (j = 1; j <= dynabomb_size; j++)
5743     {
5744       int x = ex + j * xy[i][0];
5745       int y = ey + j * xy[i][1];
5746       int element;
5747
5748       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5749         break;
5750
5751       element = Feld[x][y];
5752
5753       // do not restart explosions of fields with active bombs
5754       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5755         continue;
5756
5757       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5758
5759       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5760           !IS_DIGGABLE(element) && !dynabomb_xl)
5761         break;
5762     }
5763   }
5764 }
5765
5766 void Bang(int x, int y)
5767 {
5768   int element = MovingOrBlocked2Element(x, y);
5769   int explosion_type = EX_TYPE_NORMAL;
5770
5771   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5772   {
5773     struct PlayerInfo *player = PLAYERINFO(x, y);
5774
5775     element = Feld[x][y] = player->initial_element;
5776
5777     if (level.use_explosion_element[player->index_nr])
5778     {
5779       int explosion_element = level.explosion_element[player->index_nr];
5780
5781       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5782         explosion_type = EX_TYPE_CROSS;
5783       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5784         explosion_type = EX_TYPE_CENTER;
5785     }
5786   }
5787
5788   switch (element)
5789   {
5790     case EL_BUG:
5791     case EL_SPACESHIP:
5792     case EL_BD_BUTTERFLY:
5793     case EL_BD_FIREFLY:
5794     case EL_YAMYAM:
5795     case EL_DARK_YAMYAM:
5796     case EL_ROBOT:
5797     case EL_PACMAN:
5798     case EL_MOLE:
5799       RaiseScoreElement(element);
5800       break;
5801
5802     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5803     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5804     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5805     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5806     case EL_DYNABOMB_INCREASE_NUMBER:
5807     case EL_DYNABOMB_INCREASE_SIZE:
5808     case EL_DYNABOMB_INCREASE_POWER:
5809       explosion_type = EX_TYPE_DYNA;
5810       break;
5811
5812     case EL_DC_LANDMINE:
5813       explosion_type = EX_TYPE_CENTER;
5814       break;
5815
5816     case EL_PENGUIN:
5817     case EL_LAMP:
5818     case EL_LAMP_ACTIVE:
5819     case EL_AMOEBA_TO_DIAMOND:
5820       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5821         explosion_type = EX_TYPE_CENTER;
5822       break;
5823
5824     default:
5825       if (element_info[element].explosion_type == EXPLODES_CROSS)
5826         explosion_type = EX_TYPE_CROSS;
5827       else if (element_info[element].explosion_type == EXPLODES_1X1)
5828         explosion_type = EX_TYPE_CENTER;
5829       break;
5830   }
5831
5832   if (explosion_type == EX_TYPE_DYNA)
5833     DynaExplode(x, y);
5834   else
5835     Explode(x, y, EX_PHASE_START, explosion_type);
5836
5837   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5838 }
5839
5840 static void SplashAcid(int x, int y)
5841 {
5842   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5843       (!IN_LEV_FIELD(x - 1, y - 2) ||
5844        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5845     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5846
5847   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5848       (!IN_LEV_FIELD(x + 1, y - 2) ||
5849        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5850     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5851
5852   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5853 }
5854
5855 static void InitBeltMovement(void)
5856 {
5857   static int belt_base_element[4] =
5858   {
5859     EL_CONVEYOR_BELT_1_LEFT,
5860     EL_CONVEYOR_BELT_2_LEFT,
5861     EL_CONVEYOR_BELT_3_LEFT,
5862     EL_CONVEYOR_BELT_4_LEFT
5863   };
5864   static int belt_base_active_element[4] =
5865   {
5866     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5867     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5868     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5869     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5870   };
5871
5872   int x, y, i, j;
5873
5874   // set frame order for belt animation graphic according to belt direction
5875   for (i = 0; i < NUM_BELTS; i++)
5876   {
5877     int belt_nr = i;
5878
5879     for (j = 0; j < NUM_BELT_PARTS; j++)
5880     {
5881       int element = belt_base_active_element[belt_nr] + j;
5882       int graphic_1 = el2img(element);
5883       int graphic_2 = el2panelimg(element);
5884
5885       if (game.belt_dir[i] == MV_LEFT)
5886       {
5887         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5888         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5889       }
5890       else
5891       {
5892         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5893         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5894       }
5895     }
5896   }
5897
5898   SCAN_PLAYFIELD(x, y)
5899   {
5900     int element = Feld[x][y];
5901
5902     for (i = 0; i < NUM_BELTS; i++)
5903     {
5904       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5905       {
5906         int e_belt_nr = getBeltNrFromBeltElement(element);
5907         int belt_nr = i;
5908
5909         if (e_belt_nr == belt_nr)
5910         {
5911           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5912
5913           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5914         }
5915       }
5916     }
5917   }
5918 }
5919
5920 static void ToggleBeltSwitch(int x, int y)
5921 {
5922   static int belt_base_element[4] =
5923   {
5924     EL_CONVEYOR_BELT_1_LEFT,
5925     EL_CONVEYOR_BELT_2_LEFT,
5926     EL_CONVEYOR_BELT_3_LEFT,
5927     EL_CONVEYOR_BELT_4_LEFT
5928   };
5929   static int belt_base_active_element[4] =
5930   {
5931     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5932     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5933     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5934     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5935   };
5936   static int belt_base_switch_element[4] =
5937   {
5938     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5939     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5940     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5941     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5942   };
5943   static int belt_move_dir[4] =
5944   {
5945     MV_LEFT,
5946     MV_NONE,
5947     MV_RIGHT,
5948     MV_NONE,
5949   };
5950
5951   int element = Feld[x][y];
5952   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5953   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5954   int belt_dir = belt_move_dir[belt_dir_nr];
5955   int xx, yy, i;
5956
5957   if (!IS_BELT_SWITCH(element))
5958     return;
5959
5960   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5961   game.belt_dir[belt_nr] = belt_dir;
5962
5963   if (belt_dir_nr == 3)
5964     belt_dir_nr = 1;
5965
5966   // set frame order for belt animation graphic according to belt direction
5967   for (i = 0; i < NUM_BELT_PARTS; i++)
5968   {
5969     int element = belt_base_active_element[belt_nr] + i;
5970     int graphic_1 = el2img(element);
5971     int graphic_2 = el2panelimg(element);
5972
5973     if (belt_dir == MV_LEFT)
5974     {
5975       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5976       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5977     }
5978     else
5979     {
5980       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5981       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5982     }
5983   }
5984
5985   SCAN_PLAYFIELD(xx, yy)
5986   {
5987     int element = Feld[xx][yy];
5988
5989     if (IS_BELT_SWITCH(element))
5990     {
5991       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5992
5993       if (e_belt_nr == belt_nr)
5994       {
5995         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5996         TEST_DrawLevelField(xx, yy);
5997       }
5998     }
5999     else if (IS_BELT(element) && belt_dir != MV_NONE)
6000     {
6001       int e_belt_nr = getBeltNrFromBeltElement(element);
6002
6003       if (e_belt_nr == belt_nr)
6004       {
6005         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6006
6007         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6008         TEST_DrawLevelField(xx, yy);
6009       }
6010     }
6011     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6012     {
6013       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6014
6015       if (e_belt_nr == belt_nr)
6016       {
6017         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6018
6019         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6020         TEST_DrawLevelField(xx, yy);
6021       }
6022     }
6023   }
6024 }
6025
6026 static void ToggleSwitchgateSwitch(int x, int y)
6027 {
6028   int xx, yy;
6029
6030   game.switchgate_pos = !game.switchgate_pos;
6031
6032   SCAN_PLAYFIELD(xx, yy)
6033   {
6034     int element = Feld[xx][yy];
6035
6036     if (element == EL_SWITCHGATE_SWITCH_UP)
6037     {
6038       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6039       TEST_DrawLevelField(xx, yy);
6040     }
6041     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6042     {
6043       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6044       TEST_DrawLevelField(xx, yy);
6045     }
6046     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6047     {
6048       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6049       TEST_DrawLevelField(xx, yy);
6050     }
6051     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6052     {
6053       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6054       TEST_DrawLevelField(xx, yy);
6055     }
6056     else if (element == EL_SWITCHGATE_OPEN ||
6057              element == EL_SWITCHGATE_OPENING)
6058     {
6059       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6060
6061       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6062     }
6063     else if (element == EL_SWITCHGATE_CLOSED ||
6064              element == EL_SWITCHGATE_CLOSING)
6065     {
6066       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6067
6068       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6069     }
6070   }
6071 }
6072
6073 static int getInvisibleActiveFromInvisibleElement(int element)
6074 {
6075   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6076           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6077           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6078           element);
6079 }
6080
6081 static int getInvisibleFromInvisibleActiveElement(int element)
6082 {
6083   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6084           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6085           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6086           element);
6087 }
6088
6089 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6090 {
6091   int x, y;
6092
6093   SCAN_PLAYFIELD(x, y)
6094   {
6095     int element = Feld[x][y];
6096
6097     if (element == EL_LIGHT_SWITCH &&
6098         game.light_time_left > 0)
6099     {
6100       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6101       TEST_DrawLevelField(x, y);
6102     }
6103     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6104              game.light_time_left == 0)
6105     {
6106       Feld[x][y] = EL_LIGHT_SWITCH;
6107       TEST_DrawLevelField(x, y);
6108     }
6109     else if (element == EL_EMC_DRIPPER &&
6110              game.light_time_left > 0)
6111     {
6112       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6113       TEST_DrawLevelField(x, y);
6114     }
6115     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6116              game.light_time_left == 0)
6117     {
6118       Feld[x][y] = EL_EMC_DRIPPER;
6119       TEST_DrawLevelField(x, y);
6120     }
6121     else if (element == EL_INVISIBLE_STEELWALL ||
6122              element == EL_INVISIBLE_WALL ||
6123              element == EL_INVISIBLE_SAND)
6124     {
6125       if (game.light_time_left > 0)
6126         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6127
6128       TEST_DrawLevelField(x, y);
6129
6130       // uncrumble neighbour fields, if needed
6131       if (element == EL_INVISIBLE_SAND)
6132         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6133     }
6134     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6135              element == EL_INVISIBLE_WALL_ACTIVE ||
6136              element == EL_INVISIBLE_SAND_ACTIVE)
6137     {
6138       if (game.light_time_left == 0)
6139         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6140
6141       TEST_DrawLevelField(x, y);
6142
6143       // re-crumble neighbour fields, if needed
6144       if (element == EL_INVISIBLE_SAND)
6145         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6146     }
6147   }
6148 }
6149
6150 static void RedrawAllInvisibleElementsForLenses(void)
6151 {
6152   int x, y;
6153
6154   SCAN_PLAYFIELD(x, y)
6155   {
6156     int element = Feld[x][y];
6157
6158     if (element == EL_EMC_DRIPPER &&
6159         game.lenses_time_left > 0)
6160     {
6161       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6162       TEST_DrawLevelField(x, y);
6163     }
6164     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6165              game.lenses_time_left == 0)
6166     {
6167       Feld[x][y] = EL_EMC_DRIPPER;
6168       TEST_DrawLevelField(x, y);
6169     }
6170     else if (element == EL_INVISIBLE_STEELWALL ||
6171              element == EL_INVISIBLE_WALL ||
6172              element == EL_INVISIBLE_SAND)
6173     {
6174       if (game.lenses_time_left > 0)
6175         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6176
6177       TEST_DrawLevelField(x, y);
6178
6179       // uncrumble neighbour fields, if needed
6180       if (element == EL_INVISIBLE_SAND)
6181         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6182     }
6183     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6184              element == EL_INVISIBLE_WALL_ACTIVE ||
6185              element == EL_INVISIBLE_SAND_ACTIVE)
6186     {
6187       if (game.lenses_time_left == 0)
6188         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6189
6190       TEST_DrawLevelField(x, y);
6191
6192       // re-crumble neighbour fields, if needed
6193       if (element == EL_INVISIBLE_SAND)
6194         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6195     }
6196   }
6197 }
6198
6199 static void RedrawAllInvisibleElementsForMagnifier(void)
6200 {
6201   int x, y;
6202
6203   SCAN_PLAYFIELD(x, y)
6204   {
6205     int element = Feld[x][y];
6206
6207     if (element == EL_EMC_FAKE_GRASS &&
6208         game.magnify_time_left > 0)
6209     {
6210       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6211       TEST_DrawLevelField(x, y);
6212     }
6213     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6214              game.magnify_time_left == 0)
6215     {
6216       Feld[x][y] = EL_EMC_FAKE_GRASS;
6217       TEST_DrawLevelField(x, y);
6218     }
6219     else if (IS_GATE_GRAY(element) &&
6220              game.magnify_time_left > 0)
6221     {
6222       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6223                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6224                     IS_EM_GATE_GRAY(element) ?
6225                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6226                     IS_EMC_GATE_GRAY(element) ?
6227                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6228                     IS_DC_GATE_GRAY(element) ?
6229                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6230                     element);
6231       TEST_DrawLevelField(x, y);
6232     }
6233     else if (IS_GATE_GRAY_ACTIVE(element) &&
6234              game.magnify_time_left == 0)
6235     {
6236       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6237                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6238                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6239                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6240                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6241                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6242                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6243                     EL_DC_GATE_WHITE_GRAY :
6244                     element);
6245       TEST_DrawLevelField(x, y);
6246     }
6247   }
6248 }
6249
6250 static void ToggleLightSwitch(int x, int y)
6251 {
6252   int element = Feld[x][y];
6253
6254   game.light_time_left =
6255     (element == EL_LIGHT_SWITCH ?
6256      level.time_light * FRAMES_PER_SECOND : 0);
6257
6258   RedrawAllLightSwitchesAndInvisibleElements();
6259 }
6260
6261 static void ActivateTimegateSwitch(int x, int y)
6262 {
6263   int xx, yy;
6264
6265   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6266
6267   SCAN_PLAYFIELD(xx, yy)
6268   {
6269     int element = Feld[xx][yy];
6270
6271     if (element == EL_TIMEGATE_CLOSED ||
6272         element == EL_TIMEGATE_CLOSING)
6273     {
6274       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6275       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6276     }
6277
6278     /*
6279     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6280     {
6281       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6282       TEST_DrawLevelField(xx, yy);
6283     }
6284     */
6285
6286   }
6287
6288   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6289                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6290 }
6291
6292 static void Impact(int x, int y)
6293 {
6294   boolean last_line = (y == lev_fieldy - 1);
6295   boolean object_hit = FALSE;
6296   boolean impact = (last_line || object_hit);
6297   int element = Feld[x][y];
6298   int smashed = EL_STEELWALL;
6299
6300   if (!last_line)       // check if element below was hit
6301   {
6302     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6303       return;
6304
6305     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6306                                          MovDir[x][y + 1] != MV_DOWN ||
6307                                          MovPos[x][y + 1] <= TILEY / 2));
6308
6309     // do not smash moving elements that left the smashed field in time
6310     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6311         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6312       object_hit = FALSE;
6313
6314 #if USE_QUICKSAND_IMPACT_BUGFIX
6315     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6316     {
6317       RemoveMovingField(x, y + 1);
6318       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6319       Feld[x][y + 2] = EL_ROCK;
6320       TEST_DrawLevelField(x, y + 2);
6321
6322       object_hit = TRUE;
6323     }
6324
6325     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6326     {
6327       RemoveMovingField(x, y + 1);
6328       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6329       Feld[x][y + 2] = EL_ROCK;
6330       TEST_DrawLevelField(x, y + 2);
6331
6332       object_hit = TRUE;
6333     }
6334 #endif
6335
6336     if (object_hit)
6337       smashed = MovingOrBlocked2Element(x, y + 1);
6338
6339     impact = (last_line || object_hit);
6340   }
6341
6342   if (!last_line && smashed == EL_ACID) // element falls into acid
6343   {
6344     SplashAcid(x, y + 1);
6345     return;
6346   }
6347
6348   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6349   // only reset graphic animation if graphic really changes after impact
6350   if (impact &&
6351       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6352   {
6353     ResetGfxAnimation(x, y);
6354     TEST_DrawLevelField(x, y);
6355   }
6356
6357   if (impact && CAN_EXPLODE_IMPACT(element))
6358   {
6359     Bang(x, y);
6360     return;
6361   }
6362   else if (impact && element == EL_PEARL &&
6363            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6364   {
6365     ResetGfxAnimation(x, y);
6366
6367     Feld[x][y] = EL_PEARL_BREAKING;
6368     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6369     return;
6370   }
6371   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6372   {
6373     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6374
6375     return;
6376   }
6377
6378   if (impact && element == EL_AMOEBA_DROP)
6379   {
6380     if (object_hit && IS_PLAYER(x, y + 1))
6381       KillPlayerUnlessEnemyProtected(x, y + 1);
6382     else if (object_hit && smashed == EL_PENGUIN)
6383       Bang(x, y + 1);
6384     else
6385     {
6386       Feld[x][y] = EL_AMOEBA_GROWING;
6387       Store[x][y] = EL_AMOEBA_WET;
6388
6389       ResetRandomAnimationValue(x, y);
6390     }
6391     return;
6392   }
6393
6394   if (object_hit)               // check which object was hit
6395   {
6396     if ((CAN_PASS_MAGIC_WALL(element) && 
6397          (smashed == EL_MAGIC_WALL ||
6398           smashed == EL_BD_MAGIC_WALL)) ||
6399         (CAN_PASS_DC_MAGIC_WALL(element) &&
6400          smashed == EL_DC_MAGIC_WALL))
6401     {
6402       int xx, yy;
6403       int activated_magic_wall =
6404         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6405          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6406          EL_DC_MAGIC_WALL_ACTIVE);
6407
6408       // activate magic wall / mill
6409       SCAN_PLAYFIELD(xx, yy)
6410       {
6411         if (Feld[xx][yy] == smashed)
6412           Feld[xx][yy] = activated_magic_wall;
6413       }
6414
6415       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6416       game.magic_wall_active = TRUE;
6417
6418       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6419                             SND_MAGIC_WALL_ACTIVATING :
6420                             smashed == EL_BD_MAGIC_WALL ?
6421                             SND_BD_MAGIC_WALL_ACTIVATING :
6422                             SND_DC_MAGIC_WALL_ACTIVATING));
6423     }
6424
6425     if (IS_PLAYER(x, y + 1))
6426     {
6427       if (CAN_SMASH_PLAYER(element))
6428       {
6429         KillPlayerUnlessEnemyProtected(x, y + 1);
6430         return;
6431       }
6432     }
6433     else if (smashed == EL_PENGUIN)
6434     {
6435       if (CAN_SMASH_PLAYER(element))
6436       {
6437         Bang(x, y + 1);
6438         return;
6439       }
6440     }
6441     else if (element == EL_BD_DIAMOND)
6442     {
6443       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6444       {
6445         Bang(x, y + 1);
6446         return;
6447       }
6448     }
6449     else if (((element == EL_SP_INFOTRON ||
6450                element == EL_SP_ZONK) &&
6451               (smashed == EL_SP_SNIKSNAK ||
6452                smashed == EL_SP_ELECTRON ||
6453                smashed == EL_SP_DISK_ORANGE)) ||
6454              (element == EL_SP_INFOTRON &&
6455               smashed == EL_SP_DISK_YELLOW))
6456     {
6457       Bang(x, y + 1);
6458       return;
6459     }
6460     else if (CAN_SMASH_EVERYTHING(element))
6461     {
6462       if (IS_CLASSIC_ENEMY(smashed) ||
6463           CAN_EXPLODE_SMASHED(smashed))
6464       {
6465         Bang(x, y + 1);
6466         return;
6467       }
6468       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6469       {
6470         if (smashed == EL_LAMP ||
6471             smashed == EL_LAMP_ACTIVE)
6472         {
6473           Bang(x, y + 1);
6474           return;
6475         }
6476         else if (smashed == EL_NUT)
6477         {
6478           Feld[x][y + 1] = EL_NUT_BREAKING;
6479           PlayLevelSound(x, y, SND_NUT_BREAKING);
6480           RaiseScoreElement(EL_NUT);
6481           return;
6482         }
6483         else if (smashed == EL_PEARL)
6484         {
6485           ResetGfxAnimation(x, y);
6486
6487           Feld[x][y + 1] = EL_PEARL_BREAKING;
6488           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6489           return;
6490         }
6491         else if (smashed == EL_DIAMOND)
6492         {
6493           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6494           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6495           return;
6496         }
6497         else if (IS_BELT_SWITCH(smashed))
6498         {
6499           ToggleBeltSwitch(x, y + 1);
6500         }
6501         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6502                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6503                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6504                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6505         {
6506           ToggleSwitchgateSwitch(x, y + 1);
6507         }
6508         else if (smashed == EL_LIGHT_SWITCH ||
6509                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6510         {
6511           ToggleLightSwitch(x, y + 1);
6512         }
6513         else
6514         {
6515           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6516
6517           CheckElementChangeBySide(x, y + 1, smashed, element,
6518                                    CE_SWITCHED, CH_SIDE_TOP);
6519           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6520                                             CH_SIDE_TOP);
6521         }
6522       }
6523       else
6524       {
6525         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6526       }
6527     }
6528   }
6529
6530   // play sound of magic wall / mill
6531   if (!last_line &&
6532       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6533        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6534        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6535   {
6536     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6537       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6538     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6539       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6540     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6541       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6542
6543     return;
6544   }
6545
6546   // play sound of object that hits the ground
6547   if (last_line || object_hit)
6548     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6549 }
6550
6551 static void TurnRoundExt(int x, int y)
6552 {
6553   static struct
6554   {
6555     int dx, dy;
6556   } move_xy[] =
6557   {
6558     {  0,  0 },
6559     { -1,  0 },
6560     { +1,  0 },
6561     {  0,  0 },
6562     {  0, -1 },
6563     {  0,  0 }, { 0, 0 }, { 0, 0 },
6564     {  0, +1 }
6565   };
6566   static struct
6567   {
6568     int left, right, back;
6569   } turn[] =
6570   {
6571     { 0,        0,              0        },
6572     { MV_DOWN,  MV_UP,          MV_RIGHT },
6573     { MV_UP,    MV_DOWN,        MV_LEFT  },
6574     { 0,        0,              0        },
6575     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6576     { 0,        0,              0        },
6577     { 0,        0,              0        },
6578     { 0,        0,              0        },
6579     { MV_RIGHT, MV_LEFT,        MV_UP    }
6580   };
6581
6582   int element = Feld[x][y];
6583   int move_pattern = element_info[element].move_pattern;
6584
6585   int old_move_dir = MovDir[x][y];
6586   int left_dir  = turn[old_move_dir].left;
6587   int right_dir = turn[old_move_dir].right;
6588   int back_dir  = turn[old_move_dir].back;
6589
6590   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6591   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6592   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6593   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6594
6595   int left_x  = x + left_dx,  left_y  = y + left_dy;
6596   int right_x = x + right_dx, right_y = y + right_dy;
6597   int move_x  = x + move_dx,  move_y  = y + move_dy;
6598
6599   int xx, yy;
6600
6601   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6602   {
6603     TestIfBadThingTouchesOtherBadThing(x, y);
6604
6605     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6606       MovDir[x][y] = right_dir;
6607     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6608       MovDir[x][y] = left_dir;
6609
6610     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6611       MovDelay[x][y] = 9;
6612     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6613       MovDelay[x][y] = 1;
6614   }
6615   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6616   {
6617     TestIfBadThingTouchesOtherBadThing(x, y);
6618
6619     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6620       MovDir[x][y] = left_dir;
6621     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6622       MovDir[x][y] = right_dir;
6623
6624     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6625       MovDelay[x][y] = 9;
6626     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6627       MovDelay[x][y] = 1;
6628   }
6629   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6630   {
6631     TestIfBadThingTouchesOtherBadThing(x, y);
6632
6633     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6634       MovDir[x][y] = left_dir;
6635     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6636       MovDir[x][y] = right_dir;
6637
6638     if (MovDir[x][y] != old_move_dir)
6639       MovDelay[x][y] = 9;
6640   }
6641   else if (element == EL_YAMYAM)
6642   {
6643     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6644     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6645
6646     if (can_turn_left && can_turn_right)
6647       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6648     else if (can_turn_left)
6649       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6650     else if (can_turn_right)
6651       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6652     else
6653       MovDir[x][y] = back_dir;
6654
6655     MovDelay[x][y] = 16 + 16 * RND(3);
6656   }
6657   else if (element == EL_DARK_YAMYAM)
6658   {
6659     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6660                                                          left_x, left_y);
6661     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6662                                                          right_x, right_y);
6663
6664     if (can_turn_left && can_turn_right)
6665       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6666     else if (can_turn_left)
6667       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6668     else if (can_turn_right)
6669       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6670     else
6671       MovDir[x][y] = back_dir;
6672
6673     MovDelay[x][y] = 16 + 16 * RND(3);
6674   }
6675   else if (element == EL_PACMAN)
6676   {
6677     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6678     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6679
6680     if (can_turn_left && can_turn_right)
6681       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6682     else if (can_turn_left)
6683       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6684     else if (can_turn_right)
6685       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6686     else
6687       MovDir[x][y] = back_dir;
6688
6689     MovDelay[x][y] = 6 + RND(40);
6690   }
6691   else if (element == EL_PIG)
6692   {
6693     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6694     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6695     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6696     boolean should_turn_left, should_turn_right, should_move_on;
6697     int rnd_value = 24;
6698     int rnd = RND(rnd_value);
6699
6700     should_turn_left = (can_turn_left &&
6701                         (!can_move_on ||
6702                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6703                                                    y + back_dy + left_dy)));
6704     should_turn_right = (can_turn_right &&
6705                          (!can_move_on ||
6706                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6707                                                     y + back_dy + right_dy)));
6708     should_move_on = (can_move_on &&
6709                       (!can_turn_left ||
6710                        !can_turn_right ||
6711                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6712                                                  y + move_dy + left_dy) ||
6713                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6714                                                  y + move_dy + right_dy)));
6715
6716     if (should_turn_left || should_turn_right || should_move_on)
6717     {
6718       if (should_turn_left && should_turn_right && should_move_on)
6719         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6720                         rnd < 2 * rnd_value / 3 ? right_dir :
6721                         old_move_dir);
6722       else if (should_turn_left && should_turn_right)
6723         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6724       else if (should_turn_left && should_move_on)
6725         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6726       else if (should_turn_right && should_move_on)
6727         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6728       else if (should_turn_left)
6729         MovDir[x][y] = left_dir;
6730       else if (should_turn_right)
6731         MovDir[x][y] = right_dir;
6732       else if (should_move_on)
6733         MovDir[x][y] = old_move_dir;
6734     }
6735     else if (can_move_on && rnd > rnd_value / 8)
6736       MovDir[x][y] = old_move_dir;
6737     else if (can_turn_left && can_turn_right)
6738       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6739     else if (can_turn_left && rnd > rnd_value / 8)
6740       MovDir[x][y] = left_dir;
6741     else if (can_turn_right && rnd > rnd_value/8)
6742       MovDir[x][y] = right_dir;
6743     else
6744       MovDir[x][y] = back_dir;
6745
6746     xx = x + move_xy[MovDir[x][y]].dx;
6747     yy = y + move_xy[MovDir[x][y]].dy;
6748
6749     if (!IN_LEV_FIELD(xx, yy) ||
6750         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6751       MovDir[x][y] = old_move_dir;
6752
6753     MovDelay[x][y] = 0;
6754   }
6755   else if (element == EL_DRAGON)
6756   {
6757     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6758     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6759     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6760     int rnd_value = 24;
6761     int rnd = RND(rnd_value);
6762
6763     if (can_move_on && rnd > rnd_value / 8)
6764       MovDir[x][y] = old_move_dir;
6765     else if (can_turn_left && can_turn_right)
6766       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6767     else if (can_turn_left && rnd > rnd_value / 8)
6768       MovDir[x][y] = left_dir;
6769     else if (can_turn_right && rnd > rnd_value / 8)
6770       MovDir[x][y] = right_dir;
6771     else
6772       MovDir[x][y] = back_dir;
6773
6774     xx = x + move_xy[MovDir[x][y]].dx;
6775     yy = y + move_xy[MovDir[x][y]].dy;
6776
6777     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6778       MovDir[x][y] = old_move_dir;
6779
6780     MovDelay[x][y] = 0;
6781   }
6782   else if (element == EL_MOLE)
6783   {
6784     boolean can_move_on =
6785       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6786                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6787                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6788     if (!can_move_on)
6789     {
6790       boolean can_turn_left =
6791         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6792                               IS_AMOEBOID(Feld[left_x][left_y])));
6793
6794       boolean can_turn_right =
6795         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6796                               IS_AMOEBOID(Feld[right_x][right_y])));
6797
6798       if (can_turn_left && can_turn_right)
6799         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6800       else if (can_turn_left)
6801         MovDir[x][y] = left_dir;
6802       else
6803         MovDir[x][y] = right_dir;
6804     }
6805
6806     if (MovDir[x][y] != old_move_dir)
6807       MovDelay[x][y] = 9;
6808   }
6809   else if (element == EL_BALLOON)
6810   {
6811     MovDir[x][y] = game.wind_direction;
6812     MovDelay[x][y] = 0;
6813   }
6814   else if (element == EL_SPRING)
6815   {
6816     if (MovDir[x][y] & MV_HORIZONTAL)
6817     {
6818       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6819           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6820       {
6821         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6822         ResetGfxAnimation(move_x, move_y);
6823         TEST_DrawLevelField(move_x, move_y);
6824
6825         MovDir[x][y] = back_dir;
6826       }
6827       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6828                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6829         MovDir[x][y] = MV_NONE;
6830     }
6831
6832     MovDelay[x][y] = 0;
6833   }
6834   else if (element == EL_ROBOT ||
6835            element == EL_SATELLITE ||
6836            element == EL_PENGUIN ||
6837            element == EL_EMC_ANDROID)
6838   {
6839     int attr_x = -1, attr_y = -1;
6840
6841     if (game.all_players_gone)
6842     {
6843       attr_x = game.exit_x;
6844       attr_y = game.exit_y;
6845     }
6846     else
6847     {
6848       int i;
6849
6850       for (i = 0; i < MAX_PLAYERS; i++)
6851       {
6852         struct PlayerInfo *player = &stored_player[i];
6853         int jx = player->jx, jy = player->jy;
6854
6855         if (!player->active)
6856           continue;
6857
6858         if (attr_x == -1 ||
6859             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6860         {
6861           attr_x = jx;
6862           attr_y = jy;
6863         }
6864       }
6865     }
6866
6867     if (element == EL_ROBOT &&
6868         game.robot_wheel_x >= 0 &&
6869         game.robot_wheel_y >= 0 &&
6870         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6871          game.engine_version < VERSION_IDENT(3,1,0,0)))
6872     {
6873       attr_x = game.robot_wheel_x;
6874       attr_y = game.robot_wheel_y;
6875     }
6876
6877     if (element == EL_PENGUIN)
6878     {
6879       int i;
6880       static int xy[4][2] =
6881       {
6882         { 0, -1 },
6883         { -1, 0 },
6884         { +1, 0 },
6885         { 0, +1 }
6886       };
6887
6888       for (i = 0; i < NUM_DIRECTIONS; i++)
6889       {
6890         int ex = x + xy[i][0];
6891         int ey = y + xy[i][1];
6892
6893         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6894                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6895                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6896                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6897         {
6898           attr_x = ex;
6899           attr_y = ey;
6900           break;
6901         }
6902       }
6903     }
6904
6905     MovDir[x][y] = MV_NONE;
6906     if (attr_x < x)
6907       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6908     else if (attr_x > x)
6909       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6910     if (attr_y < y)
6911       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6912     else if (attr_y > y)
6913       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6914
6915     if (element == EL_ROBOT)
6916     {
6917       int newx, newy;
6918
6919       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6920         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6921       Moving2Blocked(x, y, &newx, &newy);
6922
6923       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6924         MovDelay[x][y] = 8 + 8 * !RND(3);
6925       else
6926         MovDelay[x][y] = 16;
6927     }
6928     else if (element == EL_PENGUIN)
6929     {
6930       int newx, newy;
6931
6932       MovDelay[x][y] = 1;
6933
6934       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6935       {
6936         boolean first_horiz = RND(2);
6937         int new_move_dir = MovDir[x][y];
6938
6939         MovDir[x][y] =
6940           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6941         Moving2Blocked(x, y, &newx, &newy);
6942
6943         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6944           return;
6945
6946         MovDir[x][y] =
6947           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6948         Moving2Blocked(x, y, &newx, &newy);
6949
6950         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6951           return;
6952
6953         MovDir[x][y] = old_move_dir;
6954         return;
6955       }
6956     }
6957     else if (element == EL_SATELLITE)
6958     {
6959       int newx, newy;
6960
6961       MovDelay[x][y] = 1;
6962
6963       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6964       {
6965         boolean first_horiz = RND(2);
6966         int new_move_dir = MovDir[x][y];
6967
6968         MovDir[x][y] =
6969           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6970         Moving2Blocked(x, y, &newx, &newy);
6971
6972         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6973           return;
6974
6975         MovDir[x][y] =
6976           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6977         Moving2Blocked(x, y, &newx, &newy);
6978
6979         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6980           return;
6981
6982         MovDir[x][y] = old_move_dir;
6983         return;
6984       }
6985     }
6986     else if (element == EL_EMC_ANDROID)
6987     {
6988       static int check_pos[16] =
6989       {
6990         -1,             //  0 => (invalid)
6991         7,              //  1 => MV_LEFT
6992         3,              //  2 => MV_RIGHT
6993         -1,             //  3 => (invalid)
6994         1,              //  4 =>            MV_UP
6995         0,              //  5 => MV_LEFT  | MV_UP
6996         2,              //  6 => MV_RIGHT | MV_UP
6997         -1,             //  7 => (invalid)
6998         5,              //  8 =>            MV_DOWN
6999         6,              //  9 => MV_LEFT  | MV_DOWN
7000         4,              // 10 => MV_RIGHT | MV_DOWN
7001         -1,             // 11 => (invalid)
7002         -1,             // 12 => (invalid)
7003         -1,             // 13 => (invalid)
7004         -1,             // 14 => (invalid)
7005         -1,             // 15 => (invalid)
7006       };
7007       static struct
7008       {
7009         int dx, dy;
7010         int dir;
7011       } check_xy[8] =
7012       {
7013         { -1, -1,       MV_LEFT  | MV_UP   },
7014         {  0, -1,                  MV_UP   },
7015         { +1, -1,       MV_RIGHT | MV_UP   },
7016         { +1,  0,       MV_RIGHT           },
7017         { +1, +1,       MV_RIGHT | MV_DOWN },
7018         {  0, +1,                  MV_DOWN },
7019         { -1, +1,       MV_LEFT  | MV_DOWN },
7020         { -1,  0,       MV_LEFT            },
7021       };
7022       int start_pos, check_order;
7023       boolean can_clone = FALSE;
7024       int i;
7025
7026       // check if there is any free field around current position
7027       for (i = 0; i < 8; i++)
7028       {
7029         int newx = x + check_xy[i].dx;
7030         int newy = y + check_xy[i].dy;
7031
7032         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7033         {
7034           can_clone = TRUE;
7035
7036           break;
7037         }
7038       }
7039
7040       if (can_clone)            // randomly find an element to clone
7041       {
7042         can_clone = FALSE;
7043
7044         start_pos = check_pos[RND(8)];
7045         check_order = (RND(2) ? -1 : +1);
7046
7047         for (i = 0; i < 8; i++)
7048         {
7049           int pos_raw = start_pos + i * check_order;
7050           int pos = (pos_raw + 8) % 8;
7051           int newx = x + check_xy[pos].dx;
7052           int newy = y + check_xy[pos].dy;
7053
7054           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7055           {
7056             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7057             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7058
7059             Store[x][y] = Feld[newx][newy];
7060
7061             can_clone = TRUE;
7062
7063             break;
7064           }
7065         }
7066       }
7067
7068       if (can_clone)            // randomly find a direction to move
7069       {
7070         can_clone = FALSE;
7071
7072         start_pos = check_pos[RND(8)];
7073         check_order = (RND(2) ? -1 : +1);
7074
7075         for (i = 0; i < 8; i++)
7076         {
7077           int pos_raw = start_pos + i * check_order;
7078           int pos = (pos_raw + 8) % 8;
7079           int newx = x + check_xy[pos].dx;
7080           int newy = y + check_xy[pos].dy;
7081           int new_move_dir = check_xy[pos].dir;
7082
7083           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7084           {
7085             MovDir[x][y] = new_move_dir;
7086             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7087
7088             can_clone = TRUE;
7089
7090             break;
7091           }
7092         }
7093       }
7094
7095       if (can_clone)            // cloning and moving successful
7096         return;
7097
7098       // cannot clone -- try to move towards player
7099
7100       start_pos = check_pos[MovDir[x][y] & 0x0f];
7101       check_order = (RND(2) ? -1 : +1);
7102
7103       for (i = 0; i < 3; i++)
7104       {
7105         // first check start_pos, then previous/next or (next/previous) pos
7106         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7107         int pos = (pos_raw + 8) % 8;
7108         int newx = x + check_xy[pos].dx;
7109         int newy = y + check_xy[pos].dy;
7110         int new_move_dir = check_xy[pos].dir;
7111
7112         if (IS_PLAYER(newx, newy))
7113           break;
7114
7115         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7116         {
7117           MovDir[x][y] = new_move_dir;
7118           MovDelay[x][y] = level.android_move_time * 8 + 1;
7119
7120           break;
7121         }
7122       }
7123     }
7124   }
7125   else if (move_pattern == MV_TURNING_LEFT ||
7126            move_pattern == MV_TURNING_RIGHT ||
7127            move_pattern == MV_TURNING_LEFT_RIGHT ||
7128            move_pattern == MV_TURNING_RIGHT_LEFT ||
7129            move_pattern == MV_TURNING_RANDOM ||
7130            move_pattern == MV_ALL_DIRECTIONS)
7131   {
7132     boolean can_turn_left =
7133       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7134     boolean can_turn_right =
7135       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7136
7137     if (element_info[element].move_stepsize == 0)       // "not moving"
7138       return;
7139
7140     if (move_pattern == MV_TURNING_LEFT)
7141       MovDir[x][y] = left_dir;
7142     else if (move_pattern == MV_TURNING_RIGHT)
7143       MovDir[x][y] = right_dir;
7144     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7145       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7146     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7147       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7148     else if (move_pattern == MV_TURNING_RANDOM)
7149       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7150                       can_turn_right && !can_turn_left ? right_dir :
7151                       RND(2) ? left_dir : right_dir);
7152     else if (can_turn_left && can_turn_right)
7153       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7154     else if (can_turn_left)
7155       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7156     else if (can_turn_right)
7157       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7158     else
7159       MovDir[x][y] = back_dir;
7160
7161     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7162   }
7163   else if (move_pattern == MV_HORIZONTAL ||
7164            move_pattern == MV_VERTICAL)
7165   {
7166     if (move_pattern & old_move_dir)
7167       MovDir[x][y] = back_dir;
7168     else if (move_pattern == MV_HORIZONTAL)
7169       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7170     else if (move_pattern == MV_VERTICAL)
7171       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7172
7173     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7174   }
7175   else if (move_pattern & MV_ANY_DIRECTION)
7176   {
7177     MovDir[x][y] = move_pattern;
7178     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7179   }
7180   else if (move_pattern & MV_WIND_DIRECTION)
7181   {
7182     MovDir[x][y] = game.wind_direction;
7183     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7184   }
7185   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7186   {
7187     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7188       MovDir[x][y] = left_dir;
7189     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7190       MovDir[x][y] = right_dir;
7191
7192     if (MovDir[x][y] != old_move_dir)
7193       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7194   }
7195   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7196   {
7197     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7198       MovDir[x][y] = right_dir;
7199     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7200       MovDir[x][y] = left_dir;
7201
7202     if (MovDir[x][y] != old_move_dir)
7203       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7204   }
7205   else if (move_pattern == MV_TOWARDS_PLAYER ||
7206            move_pattern == MV_AWAY_FROM_PLAYER)
7207   {
7208     int attr_x = -1, attr_y = -1;
7209     int newx, newy;
7210     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7211
7212     if (game.all_players_gone)
7213     {
7214       attr_x = game.exit_x;
7215       attr_y = game.exit_y;
7216     }
7217     else
7218     {
7219       int i;
7220
7221       for (i = 0; i < MAX_PLAYERS; i++)
7222       {
7223         struct PlayerInfo *player = &stored_player[i];
7224         int jx = player->jx, jy = player->jy;
7225
7226         if (!player->active)
7227           continue;
7228
7229         if (attr_x == -1 ||
7230             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7231         {
7232           attr_x = jx;
7233           attr_y = jy;
7234         }
7235       }
7236     }
7237
7238     MovDir[x][y] = MV_NONE;
7239     if (attr_x < x)
7240       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7241     else if (attr_x > x)
7242       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7243     if (attr_y < y)
7244       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7245     else if (attr_y > y)
7246       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7247
7248     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7249
7250     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7251     {
7252       boolean first_horiz = RND(2);
7253       int new_move_dir = MovDir[x][y];
7254
7255       if (element_info[element].move_stepsize == 0)     // "not moving"
7256       {
7257         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7258         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7259
7260         return;
7261       }
7262
7263       MovDir[x][y] =
7264         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7265       Moving2Blocked(x, y, &newx, &newy);
7266
7267       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7268         return;
7269
7270       MovDir[x][y] =
7271         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7272       Moving2Blocked(x, y, &newx, &newy);
7273
7274       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7275         return;
7276
7277       MovDir[x][y] = old_move_dir;
7278     }
7279   }
7280   else if (move_pattern == MV_WHEN_PUSHED ||
7281            move_pattern == MV_WHEN_DROPPED)
7282   {
7283     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7284       MovDir[x][y] = MV_NONE;
7285
7286     MovDelay[x][y] = 0;
7287   }
7288   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7289   {
7290     static int test_xy[7][2] =
7291     {
7292       { 0, -1 },
7293       { -1, 0 },
7294       { +1, 0 },
7295       { 0, +1 },
7296       { 0, -1 },
7297       { -1, 0 },
7298       { +1, 0 },
7299     };
7300     static int test_dir[7] =
7301     {
7302       MV_UP,
7303       MV_LEFT,
7304       MV_RIGHT,
7305       MV_DOWN,
7306       MV_UP,
7307       MV_LEFT,
7308       MV_RIGHT,
7309     };
7310     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7311     int move_preference = -1000000;     // start with very low preference
7312     int new_move_dir = MV_NONE;
7313     int start_test = RND(4);
7314     int i;
7315
7316     for (i = 0; i < NUM_DIRECTIONS; i++)
7317     {
7318       int move_dir = test_dir[start_test + i];
7319       int move_dir_preference;
7320
7321       xx = x + test_xy[start_test + i][0];
7322       yy = y + test_xy[start_test + i][1];
7323
7324       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7325           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7326       {
7327         new_move_dir = move_dir;
7328
7329         break;
7330       }
7331
7332       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7333         continue;
7334
7335       move_dir_preference = -1 * RunnerVisit[xx][yy];
7336       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7337         move_dir_preference = PlayerVisit[xx][yy];
7338
7339       if (move_dir_preference > move_preference)
7340       {
7341         // prefer field that has not been visited for the longest time
7342         move_preference = move_dir_preference;
7343         new_move_dir = move_dir;
7344       }
7345       else if (move_dir_preference == move_preference &&
7346                move_dir == old_move_dir)
7347       {
7348         // prefer last direction when all directions are preferred equally
7349         move_preference = move_dir_preference;
7350         new_move_dir = move_dir;
7351       }
7352     }
7353
7354     MovDir[x][y] = new_move_dir;
7355     if (old_move_dir != new_move_dir)
7356       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7357   }
7358 }
7359
7360 static void TurnRound(int x, int y)
7361 {
7362   int direction = MovDir[x][y];
7363
7364   TurnRoundExt(x, y);
7365
7366   GfxDir[x][y] = MovDir[x][y];
7367
7368   if (direction != MovDir[x][y])
7369     GfxFrame[x][y] = 0;
7370
7371   if (MovDelay[x][y])
7372     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7373
7374   ResetGfxFrame(x, y);
7375 }
7376
7377 static boolean JustBeingPushed(int x, int y)
7378 {
7379   int i;
7380
7381   for (i = 0; i < MAX_PLAYERS; i++)
7382   {
7383     struct PlayerInfo *player = &stored_player[i];
7384
7385     if (player->active && player->is_pushing && player->MovPos)
7386     {
7387       int next_jx = player->jx + (player->jx - player->last_jx);
7388       int next_jy = player->jy + (player->jy - player->last_jy);
7389
7390       if (x == next_jx && y == next_jy)
7391         return TRUE;
7392     }
7393   }
7394
7395   return FALSE;
7396 }
7397
7398 static void StartMoving(int x, int y)
7399 {
7400   boolean started_moving = FALSE;       // some elements can fall _and_ move
7401   int element = Feld[x][y];
7402
7403   if (Stop[x][y])
7404     return;
7405
7406   if (MovDelay[x][y] == 0)
7407     GfxAction[x][y] = ACTION_DEFAULT;
7408
7409   if (CAN_FALL(element) && y < lev_fieldy - 1)
7410   {
7411     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7412         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7413       if (JustBeingPushed(x, y))
7414         return;
7415
7416     if (element == EL_QUICKSAND_FULL)
7417     {
7418       if (IS_FREE(x, y + 1))
7419       {
7420         InitMovingField(x, y, MV_DOWN);
7421         started_moving = TRUE;
7422
7423         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7424 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7425         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7426           Store[x][y] = EL_ROCK;
7427 #else
7428         Store[x][y] = EL_ROCK;
7429 #endif
7430
7431         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7432       }
7433       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7434       {
7435         if (!MovDelay[x][y])
7436         {
7437           MovDelay[x][y] = TILEY + 1;
7438
7439           ResetGfxAnimation(x, y);
7440           ResetGfxAnimation(x, y + 1);
7441         }
7442
7443         if (MovDelay[x][y])
7444         {
7445           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7446           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7447
7448           MovDelay[x][y]--;
7449           if (MovDelay[x][y])
7450             return;
7451         }
7452
7453         Feld[x][y] = EL_QUICKSAND_EMPTY;
7454         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7455         Store[x][y + 1] = Store[x][y];
7456         Store[x][y] = 0;
7457
7458         PlayLevelSoundAction(x, y, ACTION_FILLING);
7459       }
7460       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7461       {
7462         if (!MovDelay[x][y])
7463         {
7464           MovDelay[x][y] = TILEY + 1;
7465
7466           ResetGfxAnimation(x, y);
7467           ResetGfxAnimation(x, y + 1);
7468         }
7469
7470         if (MovDelay[x][y])
7471         {
7472           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7473           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7474
7475           MovDelay[x][y]--;
7476           if (MovDelay[x][y])
7477             return;
7478         }
7479
7480         Feld[x][y] = EL_QUICKSAND_EMPTY;
7481         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7482         Store[x][y + 1] = Store[x][y];
7483         Store[x][y] = 0;
7484
7485         PlayLevelSoundAction(x, y, ACTION_FILLING);
7486       }
7487     }
7488     else if (element == EL_QUICKSAND_FAST_FULL)
7489     {
7490       if (IS_FREE(x, y + 1))
7491       {
7492         InitMovingField(x, y, MV_DOWN);
7493         started_moving = TRUE;
7494
7495         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7496 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7497         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7498           Store[x][y] = EL_ROCK;
7499 #else
7500         Store[x][y] = EL_ROCK;
7501 #endif
7502
7503         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7504       }
7505       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7506       {
7507         if (!MovDelay[x][y])
7508         {
7509           MovDelay[x][y] = TILEY + 1;
7510
7511           ResetGfxAnimation(x, y);
7512           ResetGfxAnimation(x, y + 1);
7513         }
7514
7515         if (MovDelay[x][y])
7516         {
7517           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7518           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7519
7520           MovDelay[x][y]--;
7521           if (MovDelay[x][y])
7522             return;
7523         }
7524
7525         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7526         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7527         Store[x][y + 1] = Store[x][y];
7528         Store[x][y] = 0;
7529
7530         PlayLevelSoundAction(x, y, ACTION_FILLING);
7531       }
7532       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7533       {
7534         if (!MovDelay[x][y])
7535         {
7536           MovDelay[x][y] = TILEY + 1;
7537
7538           ResetGfxAnimation(x, y);
7539           ResetGfxAnimation(x, y + 1);
7540         }
7541
7542         if (MovDelay[x][y])
7543         {
7544           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7545           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7546
7547           MovDelay[x][y]--;
7548           if (MovDelay[x][y])
7549             return;
7550         }
7551
7552         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7553         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7554         Store[x][y + 1] = Store[x][y];
7555         Store[x][y] = 0;
7556
7557         PlayLevelSoundAction(x, y, ACTION_FILLING);
7558       }
7559     }
7560     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7561              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7562     {
7563       InitMovingField(x, y, MV_DOWN);
7564       started_moving = TRUE;
7565
7566       Feld[x][y] = EL_QUICKSAND_FILLING;
7567       Store[x][y] = element;
7568
7569       PlayLevelSoundAction(x, y, ACTION_FILLING);
7570     }
7571     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7572              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7573     {
7574       InitMovingField(x, y, MV_DOWN);
7575       started_moving = TRUE;
7576
7577       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7578       Store[x][y] = element;
7579
7580       PlayLevelSoundAction(x, y, ACTION_FILLING);
7581     }
7582     else if (element == EL_MAGIC_WALL_FULL)
7583     {
7584       if (IS_FREE(x, y + 1))
7585       {
7586         InitMovingField(x, y, MV_DOWN);
7587         started_moving = TRUE;
7588
7589         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7590         Store[x][y] = EL_CHANGED(Store[x][y]);
7591       }
7592       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7593       {
7594         if (!MovDelay[x][y])
7595           MovDelay[x][y] = TILEY / 4 + 1;
7596
7597         if (MovDelay[x][y])
7598         {
7599           MovDelay[x][y]--;
7600           if (MovDelay[x][y])
7601             return;
7602         }
7603
7604         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7605         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7606         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7607         Store[x][y] = 0;
7608       }
7609     }
7610     else if (element == EL_BD_MAGIC_WALL_FULL)
7611     {
7612       if (IS_FREE(x, y + 1))
7613       {
7614         InitMovingField(x, y, MV_DOWN);
7615         started_moving = TRUE;
7616
7617         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7618         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7619       }
7620       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7621       {
7622         if (!MovDelay[x][y])
7623           MovDelay[x][y] = TILEY / 4 + 1;
7624
7625         if (MovDelay[x][y])
7626         {
7627           MovDelay[x][y]--;
7628           if (MovDelay[x][y])
7629             return;
7630         }
7631
7632         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7633         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7634         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7635         Store[x][y] = 0;
7636       }
7637     }
7638     else if (element == EL_DC_MAGIC_WALL_FULL)
7639     {
7640       if (IS_FREE(x, y + 1))
7641       {
7642         InitMovingField(x, y, MV_DOWN);
7643         started_moving = TRUE;
7644
7645         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7646         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7647       }
7648       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7649       {
7650         if (!MovDelay[x][y])
7651           MovDelay[x][y] = TILEY / 4 + 1;
7652
7653         if (MovDelay[x][y])
7654         {
7655           MovDelay[x][y]--;
7656           if (MovDelay[x][y])
7657             return;
7658         }
7659
7660         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7661         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7662         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7663         Store[x][y] = 0;
7664       }
7665     }
7666     else if ((CAN_PASS_MAGIC_WALL(element) &&
7667               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7668                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7669              (CAN_PASS_DC_MAGIC_WALL(element) &&
7670               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7671
7672     {
7673       InitMovingField(x, y, MV_DOWN);
7674       started_moving = TRUE;
7675
7676       Feld[x][y] =
7677         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7678          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7679          EL_DC_MAGIC_WALL_FILLING);
7680       Store[x][y] = element;
7681     }
7682     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7683     {
7684       SplashAcid(x, y + 1);
7685
7686       InitMovingField(x, y, MV_DOWN);
7687       started_moving = TRUE;
7688
7689       Store[x][y] = EL_ACID;
7690     }
7691     else if (
7692              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7693               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7694              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7695               CAN_FALL(element) && WasJustFalling[x][y] &&
7696               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7697
7698              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7699               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7700               (Feld[x][y + 1] == EL_BLOCKED)))
7701     {
7702       /* this is needed for a special case not covered by calling "Impact()"
7703          from "ContinueMoving()": if an element moves to a tile directly below
7704          another element which was just falling on that tile (which was empty
7705          in the previous frame), the falling element above would just stop
7706          instead of smashing the element below (in previous version, the above
7707          element was just checked for "moving" instead of "falling", resulting
7708          in incorrect smashes caused by horizontal movement of the above
7709          element; also, the case of the player being the element to smash was
7710          simply not covered here... :-/ ) */
7711
7712       CheckCollision[x][y] = 0;
7713       CheckImpact[x][y] = 0;
7714
7715       Impact(x, y);
7716     }
7717     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7718     {
7719       if (MovDir[x][y] == MV_NONE)
7720       {
7721         InitMovingField(x, y, MV_DOWN);
7722         started_moving = TRUE;
7723       }
7724     }
7725     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7726     {
7727       if (WasJustFalling[x][y]) // prevent animation from being restarted
7728         MovDir[x][y] = MV_DOWN;
7729
7730       InitMovingField(x, y, MV_DOWN);
7731       started_moving = TRUE;
7732     }
7733     else if (element == EL_AMOEBA_DROP)
7734     {
7735       Feld[x][y] = EL_AMOEBA_GROWING;
7736       Store[x][y] = EL_AMOEBA_WET;
7737     }
7738     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7739               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7740              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7741              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7742     {
7743       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7744                                 (IS_FREE(x - 1, y + 1) ||
7745                                  Feld[x - 1][y + 1] == EL_ACID));
7746       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7747                                 (IS_FREE(x + 1, y + 1) ||
7748                                  Feld[x + 1][y + 1] == EL_ACID));
7749       boolean can_fall_any  = (can_fall_left || can_fall_right);
7750       boolean can_fall_both = (can_fall_left && can_fall_right);
7751       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7752
7753       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7754       {
7755         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7756           can_fall_right = FALSE;
7757         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7758           can_fall_left = FALSE;
7759         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7760           can_fall_right = FALSE;
7761         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7762           can_fall_left = FALSE;
7763
7764         can_fall_any  = (can_fall_left || can_fall_right);
7765         can_fall_both = FALSE;
7766       }
7767
7768       if (can_fall_both)
7769       {
7770         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7771           can_fall_right = FALSE;       // slip down on left side
7772         else
7773           can_fall_left = !(can_fall_right = RND(2));
7774
7775         can_fall_both = FALSE;
7776       }
7777
7778       if (can_fall_any)
7779       {
7780         // if not determined otherwise, prefer left side for slipping down
7781         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7782         started_moving = TRUE;
7783       }
7784     }
7785     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7786     {
7787       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7788       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7789       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7790       int belt_dir = game.belt_dir[belt_nr];
7791
7792       if ((belt_dir == MV_LEFT  && left_is_free) ||
7793           (belt_dir == MV_RIGHT && right_is_free))
7794       {
7795         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7796
7797         InitMovingField(x, y, belt_dir);
7798         started_moving = TRUE;
7799
7800         Pushed[x][y] = TRUE;
7801         Pushed[nextx][y] = TRUE;
7802
7803         GfxAction[x][y] = ACTION_DEFAULT;
7804       }
7805       else
7806       {
7807         MovDir[x][y] = 0;       // if element was moving, stop it
7808       }
7809     }
7810   }
7811
7812   // not "else if" because of elements that can fall and move (EL_SPRING)
7813   if (CAN_MOVE(element) && !started_moving)
7814   {
7815     int move_pattern = element_info[element].move_pattern;
7816     int newx, newy;
7817
7818     Moving2Blocked(x, y, &newx, &newy);
7819
7820     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7821       return;
7822
7823     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7824         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7825     {
7826       WasJustMoving[x][y] = 0;
7827       CheckCollision[x][y] = 0;
7828
7829       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7830
7831       if (Feld[x][y] != element)        // element has changed
7832         return;
7833     }
7834
7835     if (!MovDelay[x][y])        // start new movement phase
7836     {
7837       // all objects that can change their move direction after each step
7838       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7839
7840       if (element != EL_YAMYAM &&
7841           element != EL_DARK_YAMYAM &&
7842           element != EL_PACMAN &&
7843           !(move_pattern & MV_ANY_DIRECTION) &&
7844           move_pattern != MV_TURNING_LEFT &&
7845           move_pattern != MV_TURNING_RIGHT &&
7846           move_pattern != MV_TURNING_LEFT_RIGHT &&
7847           move_pattern != MV_TURNING_RIGHT_LEFT &&
7848           move_pattern != MV_TURNING_RANDOM)
7849       {
7850         TurnRound(x, y);
7851
7852         if (MovDelay[x][y] && (element == EL_BUG ||
7853                                element == EL_SPACESHIP ||
7854                                element == EL_SP_SNIKSNAK ||
7855                                element == EL_SP_ELECTRON ||
7856                                element == EL_MOLE))
7857           TEST_DrawLevelField(x, y);
7858       }
7859     }
7860
7861     if (MovDelay[x][y])         // wait some time before next movement
7862     {
7863       MovDelay[x][y]--;
7864
7865       if (element == EL_ROBOT ||
7866           element == EL_YAMYAM ||
7867           element == EL_DARK_YAMYAM)
7868       {
7869         DrawLevelElementAnimationIfNeeded(x, y, element);
7870         PlayLevelSoundAction(x, y, ACTION_WAITING);
7871       }
7872       else if (element == EL_SP_ELECTRON)
7873         DrawLevelElementAnimationIfNeeded(x, y, element);
7874       else if (element == EL_DRAGON)
7875       {
7876         int i;
7877         int dir = MovDir[x][y];
7878         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7879         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7880         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7881                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7882                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7883                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7884         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7885
7886         GfxAction[x][y] = ACTION_ATTACKING;
7887
7888         if (IS_PLAYER(x, y))
7889           DrawPlayerField(x, y);
7890         else
7891           TEST_DrawLevelField(x, y);
7892
7893         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7894
7895         for (i = 1; i <= 3; i++)
7896         {
7897           int xx = x + i * dx;
7898           int yy = y + i * dy;
7899           int sx = SCREENX(xx);
7900           int sy = SCREENY(yy);
7901           int flame_graphic = graphic + (i - 1);
7902
7903           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7904             break;
7905
7906           if (MovDelay[x][y])
7907           {
7908             int flamed = MovingOrBlocked2Element(xx, yy);
7909
7910             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7911               Bang(xx, yy);
7912             else
7913               RemoveMovingField(xx, yy);
7914
7915             ChangeDelay[xx][yy] = 0;
7916
7917             Feld[xx][yy] = EL_FLAMES;
7918
7919             if (IN_SCR_FIELD(sx, sy))
7920             {
7921               TEST_DrawLevelFieldCrumbled(xx, yy);
7922               DrawGraphic(sx, sy, flame_graphic, frame);
7923             }
7924           }
7925           else
7926           {
7927             if (Feld[xx][yy] == EL_FLAMES)
7928               Feld[xx][yy] = EL_EMPTY;
7929             TEST_DrawLevelField(xx, yy);
7930           }
7931         }
7932       }
7933
7934       if (MovDelay[x][y])       // element still has to wait some time
7935       {
7936         PlayLevelSoundAction(x, y, ACTION_WAITING);
7937
7938         return;
7939       }
7940     }
7941
7942     // now make next step
7943
7944     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7945
7946     if (DONT_COLLIDE_WITH(element) &&
7947         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7948         !PLAYER_ENEMY_PROTECTED(newx, newy))
7949     {
7950       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7951
7952       return;
7953     }
7954
7955     else if (CAN_MOVE_INTO_ACID(element) &&
7956              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7957              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7958              (MovDir[x][y] == MV_DOWN ||
7959               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7960     {
7961       SplashAcid(newx, newy);
7962       Store[x][y] = EL_ACID;
7963     }
7964     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7965     {
7966       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7967           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7968           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7969           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7970       {
7971         RemoveField(x, y);
7972         TEST_DrawLevelField(x, y);
7973
7974         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7975         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7976           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7977
7978         game.friends_still_needed--;
7979         if (!game.friends_still_needed &&
7980             !game.GameOver &&
7981             game.all_players_gone)
7982           LevelSolved();
7983
7984         return;
7985       }
7986       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7987       {
7988         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7989           TEST_DrawLevelField(newx, newy);
7990         else
7991           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7992       }
7993       else if (!IS_FREE(newx, newy))
7994       {
7995         GfxAction[x][y] = ACTION_WAITING;
7996
7997         if (IS_PLAYER(x, y))
7998           DrawPlayerField(x, y);
7999         else
8000           TEST_DrawLevelField(x, y);
8001
8002         return;
8003       }
8004     }
8005     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8006     {
8007       if (IS_FOOD_PIG(Feld[newx][newy]))
8008       {
8009         if (IS_MOVING(newx, newy))
8010           RemoveMovingField(newx, newy);
8011         else
8012         {
8013           Feld[newx][newy] = EL_EMPTY;
8014           TEST_DrawLevelField(newx, newy);
8015         }
8016
8017         PlayLevelSound(x, y, SND_PIG_DIGGING);
8018       }
8019       else if (!IS_FREE(newx, newy))
8020       {
8021         if (IS_PLAYER(x, y))
8022           DrawPlayerField(x, y);
8023         else
8024           TEST_DrawLevelField(x, y);
8025
8026         return;
8027       }
8028     }
8029     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8030     {
8031       if (Store[x][y] != EL_EMPTY)
8032       {
8033         boolean can_clone = FALSE;
8034         int xx, yy;
8035
8036         // check if element to clone is still there
8037         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8038         {
8039           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8040           {
8041             can_clone = TRUE;
8042
8043             break;
8044           }
8045         }
8046
8047         // cannot clone or target field not free anymore -- do not clone
8048         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8049           Store[x][y] = EL_EMPTY;
8050       }
8051
8052       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8053       {
8054         if (IS_MV_DIAGONAL(MovDir[x][y]))
8055         {
8056           int diagonal_move_dir = MovDir[x][y];
8057           int stored = Store[x][y];
8058           int change_delay = 8;
8059           int graphic;
8060
8061           // android is moving diagonally
8062
8063           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8064
8065           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8066           GfxElement[x][y] = EL_EMC_ANDROID;
8067           GfxAction[x][y] = ACTION_SHRINKING;
8068           GfxDir[x][y] = diagonal_move_dir;
8069           ChangeDelay[x][y] = change_delay;
8070
8071           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8072                                    GfxDir[x][y]);
8073
8074           DrawLevelGraphicAnimation(x, y, graphic);
8075           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8076
8077           if (Feld[newx][newy] == EL_ACID)
8078           {
8079             SplashAcid(newx, newy);
8080
8081             return;
8082           }
8083
8084           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8085
8086           Store[newx][newy] = EL_EMC_ANDROID;
8087           GfxElement[newx][newy] = EL_EMC_ANDROID;
8088           GfxAction[newx][newy] = ACTION_GROWING;
8089           GfxDir[newx][newy] = diagonal_move_dir;
8090           ChangeDelay[newx][newy] = change_delay;
8091
8092           graphic = el_act_dir2img(GfxElement[newx][newy],
8093                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8094
8095           DrawLevelGraphicAnimation(newx, newy, graphic);
8096           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8097
8098           return;
8099         }
8100         else
8101         {
8102           Feld[newx][newy] = EL_EMPTY;
8103           TEST_DrawLevelField(newx, newy);
8104
8105           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8106         }
8107       }
8108       else if (!IS_FREE(newx, newy))
8109       {
8110         return;
8111       }
8112     }
8113     else if (IS_CUSTOM_ELEMENT(element) &&
8114              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8115     {
8116       if (!DigFieldByCE(newx, newy, element))
8117         return;
8118
8119       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8120       {
8121         RunnerVisit[x][y] = FrameCounter;
8122         PlayerVisit[x][y] /= 8;         // expire player visit path
8123       }
8124     }
8125     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8126     {
8127       if (!IS_FREE(newx, newy))
8128       {
8129         if (IS_PLAYER(x, y))
8130           DrawPlayerField(x, y);
8131         else
8132           TEST_DrawLevelField(x, y);
8133
8134         return;
8135       }
8136       else
8137       {
8138         boolean wanna_flame = !RND(10);
8139         int dx = newx - x, dy = newy - y;
8140         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8141         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8142         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8143                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8144         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8145                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8146
8147         if ((wanna_flame ||
8148              IS_CLASSIC_ENEMY(element1) ||
8149              IS_CLASSIC_ENEMY(element2)) &&
8150             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8151             element1 != EL_FLAMES && element2 != EL_FLAMES)
8152         {
8153           ResetGfxAnimation(x, y);
8154           GfxAction[x][y] = ACTION_ATTACKING;
8155
8156           if (IS_PLAYER(x, y))
8157             DrawPlayerField(x, y);
8158           else
8159             TEST_DrawLevelField(x, y);
8160
8161           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8162
8163           MovDelay[x][y] = 50;
8164
8165           Feld[newx][newy] = EL_FLAMES;
8166           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8167             Feld[newx1][newy1] = EL_FLAMES;
8168           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8169             Feld[newx2][newy2] = EL_FLAMES;
8170
8171           return;
8172         }
8173       }
8174     }
8175     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8176              Feld[newx][newy] == EL_DIAMOND)
8177     {
8178       if (IS_MOVING(newx, newy))
8179         RemoveMovingField(newx, newy);
8180       else
8181       {
8182         Feld[newx][newy] = EL_EMPTY;
8183         TEST_DrawLevelField(newx, newy);
8184       }
8185
8186       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8187     }
8188     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8189              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8190     {
8191       if (AmoebaNr[newx][newy])
8192       {
8193         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8194         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8195             Feld[newx][newy] == EL_BD_AMOEBA)
8196           AmoebaCnt[AmoebaNr[newx][newy]]--;
8197       }
8198
8199       if (IS_MOVING(newx, newy))
8200       {
8201         RemoveMovingField(newx, newy);
8202       }
8203       else
8204       {
8205         Feld[newx][newy] = EL_EMPTY;
8206         TEST_DrawLevelField(newx, newy);
8207       }
8208
8209       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8210     }
8211     else if ((element == EL_PACMAN || element == EL_MOLE)
8212              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8213     {
8214       if (AmoebaNr[newx][newy])
8215       {
8216         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8217         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8218             Feld[newx][newy] == EL_BD_AMOEBA)
8219           AmoebaCnt[AmoebaNr[newx][newy]]--;
8220       }
8221
8222       if (element == EL_MOLE)
8223       {
8224         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8225         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8226
8227         ResetGfxAnimation(x, y);
8228         GfxAction[x][y] = ACTION_DIGGING;
8229         TEST_DrawLevelField(x, y);
8230
8231         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8232
8233         return;                         // wait for shrinking amoeba
8234       }
8235       else      // element == EL_PACMAN
8236       {
8237         Feld[newx][newy] = EL_EMPTY;
8238         TEST_DrawLevelField(newx, newy);
8239         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8240       }
8241     }
8242     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8243              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8244               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8245     {
8246       // wait for shrinking amoeba to completely disappear
8247       return;
8248     }
8249     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8250     {
8251       // object was running against a wall
8252
8253       TurnRound(x, y);
8254
8255       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8256         DrawLevelElementAnimation(x, y, element);
8257
8258       if (DONT_TOUCH(element))
8259         TestIfBadThingTouchesPlayer(x, y);
8260
8261       return;
8262     }
8263
8264     InitMovingField(x, y, MovDir[x][y]);
8265
8266     PlayLevelSoundAction(x, y, ACTION_MOVING);
8267   }
8268
8269   if (MovDir[x][y])
8270     ContinueMoving(x, y);
8271 }
8272
8273 void ContinueMoving(int x, int y)
8274 {
8275   int element = Feld[x][y];
8276   struct ElementInfo *ei = &element_info[element];
8277   int direction = MovDir[x][y];
8278   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8279   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8280   int newx = x + dx, newy = y + dy;
8281   int stored = Store[x][y];
8282   int stored_new = Store[newx][newy];
8283   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8284   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8285   boolean last_line = (newy == lev_fieldy - 1);
8286
8287   MovPos[x][y] += getElementMoveStepsize(x, y);
8288
8289   if (pushed_by_player) // special case: moving object pushed by player
8290     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8291
8292   if (ABS(MovPos[x][y]) < TILEX)
8293   {
8294     TEST_DrawLevelField(x, y);
8295
8296     return;     // element is still moving
8297   }
8298
8299   // element reached destination field
8300
8301   Feld[x][y] = EL_EMPTY;
8302   Feld[newx][newy] = element;
8303   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8304
8305   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8306   {
8307     element = Feld[newx][newy] = EL_ACID;
8308   }
8309   else if (element == EL_MOLE)
8310   {
8311     Feld[x][y] = EL_SAND;
8312
8313     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8314   }
8315   else if (element == EL_QUICKSAND_FILLING)
8316   {
8317     element = Feld[newx][newy] = get_next_element(element);
8318     Store[newx][newy] = Store[x][y];
8319   }
8320   else if (element == EL_QUICKSAND_EMPTYING)
8321   {
8322     Feld[x][y] = get_next_element(element);
8323     element = Feld[newx][newy] = Store[x][y];
8324   }
8325   else if (element == EL_QUICKSAND_FAST_FILLING)
8326   {
8327     element = Feld[newx][newy] = get_next_element(element);
8328     Store[newx][newy] = Store[x][y];
8329   }
8330   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8331   {
8332     Feld[x][y] = get_next_element(element);
8333     element = Feld[newx][newy] = Store[x][y];
8334   }
8335   else if (element == EL_MAGIC_WALL_FILLING)
8336   {
8337     element = Feld[newx][newy] = get_next_element(element);
8338     if (!game.magic_wall_active)
8339       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8340     Store[newx][newy] = Store[x][y];
8341   }
8342   else if (element == EL_MAGIC_WALL_EMPTYING)
8343   {
8344     Feld[x][y] = get_next_element(element);
8345     if (!game.magic_wall_active)
8346       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8347     element = Feld[newx][newy] = Store[x][y];
8348
8349     InitField(newx, newy, FALSE);
8350   }
8351   else if (element == EL_BD_MAGIC_WALL_FILLING)
8352   {
8353     element = Feld[newx][newy] = get_next_element(element);
8354     if (!game.magic_wall_active)
8355       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8356     Store[newx][newy] = Store[x][y];
8357   }
8358   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8359   {
8360     Feld[x][y] = get_next_element(element);
8361     if (!game.magic_wall_active)
8362       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8363     element = Feld[newx][newy] = Store[x][y];
8364
8365     InitField(newx, newy, FALSE);
8366   }
8367   else if (element == EL_DC_MAGIC_WALL_FILLING)
8368   {
8369     element = Feld[newx][newy] = get_next_element(element);
8370     if (!game.magic_wall_active)
8371       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8372     Store[newx][newy] = Store[x][y];
8373   }
8374   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8375   {
8376     Feld[x][y] = get_next_element(element);
8377     if (!game.magic_wall_active)
8378       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8379     element = Feld[newx][newy] = Store[x][y];
8380
8381     InitField(newx, newy, FALSE);
8382   }
8383   else if (element == EL_AMOEBA_DROPPING)
8384   {
8385     Feld[x][y] = get_next_element(element);
8386     element = Feld[newx][newy] = Store[x][y];
8387   }
8388   else if (element == EL_SOKOBAN_OBJECT)
8389   {
8390     if (Back[x][y])
8391       Feld[x][y] = Back[x][y];
8392
8393     if (Back[newx][newy])
8394       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8395
8396     Back[x][y] = Back[newx][newy] = 0;
8397   }
8398
8399   Store[x][y] = EL_EMPTY;
8400   MovPos[x][y] = 0;
8401   MovDir[x][y] = 0;
8402   MovDelay[x][y] = 0;
8403
8404   MovDelay[newx][newy] = 0;
8405
8406   if (CAN_CHANGE_OR_HAS_ACTION(element))
8407   {
8408     // copy element change control values to new field
8409     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8410     ChangePage[newx][newy]  = ChangePage[x][y];
8411     ChangeCount[newx][newy] = ChangeCount[x][y];
8412     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8413   }
8414
8415   CustomValue[newx][newy] = CustomValue[x][y];
8416
8417   ChangeDelay[x][y] = 0;
8418   ChangePage[x][y] = -1;
8419   ChangeCount[x][y] = 0;
8420   ChangeEvent[x][y] = -1;
8421
8422   CustomValue[x][y] = 0;
8423
8424   // copy animation control values to new field
8425   GfxFrame[newx][newy]  = GfxFrame[x][y];
8426   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8427   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8428   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8429
8430   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8431
8432   // some elements can leave other elements behind after moving
8433   if (ei->move_leave_element != EL_EMPTY &&
8434       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8435       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8436   {
8437     int move_leave_element = ei->move_leave_element;
8438
8439     // this makes it possible to leave the removed element again
8440     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8441       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8442
8443     Feld[x][y] = move_leave_element;
8444
8445     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8446       MovDir[x][y] = direction;
8447
8448     InitField(x, y, FALSE);
8449
8450     if (GFX_CRUMBLED(Feld[x][y]))
8451       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8452
8453     if (ELEM_IS_PLAYER(move_leave_element))
8454       RelocatePlayer(x, y, move_leave_element);
8455   }
8456
8457   // do this after checking for left-behind element
8458   ResetGfxAnimation(x, y);      // reset animation values for old field
8459
8460   if (!CAN_MOVE(element) ||
8461       (CAN_FALL(element) && direction == MV_DOWN &&
8462        (element == EL_SPRING ||
8463         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8464         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8465     GfxDir[x][y] = MovDir[newx][newy] = 0;
8466
8467   TEST_DrawLevelField(x, y);
8468   TEST_DrawLevelField(newx, newy);
8469
8470   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8471
8472   // prevent pushed element from moving on in pushed direction
8473   if (pushed_by_player && CAN_MOVE(element) &&
8474       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8475       !(element_info[element].move_pattern & direction))
8476     TurnRound(newx, newy);
8477
8478   // prevent elements on conveyor belt from moving on in last direction
8479   if (pushed_by_conveyor && CAN_FALL(element) &&
8480       direction & MV_HORIZONTAL)
8481     MovDir[newx][newy] = 0;
8482
8483   if (!pushed_by_player)
8484   {
8485     int nextx = newx + dx, nexty = newy + dy;
8486     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8487
8488     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8489
8490     if (CAN_FALL(element) && direction == MV_DOWN)
8491       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8492
8493     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8494       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8495
8496     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8497       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8498   }
8499
8500   if (DONT_TOUCH(element))      // object may be nasty to player or others
8501   {
8502     TestIfBadThingTouchesPlayer(newx, newy);
8503     TestIfBadThingTouchesFriend(newx, newy);
8504
8505     if (!IS_CUSTOM_ELEMENT(element))
8506       TestIfBadThingTouchesOtherBadThing(newx, newy);
8507   }
8508   else if (element == EL_PENGUIN)
8509     TestIfFriendTouchesBadThing(newx, newy);
8510
8511   if (DONT_GET_HIT_BY(element))
8512   {
8513     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8514   }
8515
8516   // give the player one last chance (one more frame) to move away
8517   if (CAN_FALL(element) && direction == MV_DOWN &&
8518       (last_line || (!IS_FREE(x, newy + 1) &&
8519                      (!IS_PLAYER(x, newy + 1) ||
8520                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8521     Impact(x, newy);
8522
8523   if (pushed_by_player && !game.use_change_when_pushing_bug)
8524   {
8525     int push_side = MV_DIR_OPPOSITE(direction);
8526     struct PlayerInfo *player = PLAYERINFO(x, y);
8527
8528     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8529                                player->index_bit, push_side);
8530     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8531                                         player->index_bit, push_side);
8532   }
8533
8534   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8535     MovDelay[newx][newy] = 1;
8536
8537   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8538
8539   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8540   TestIfElementHitsCustomElement(newx, newy, direction);
8541   TestIfPlayerTouchesCustomElement(newx, newy);
8542   TestIfElementTouchesCustomElement(newx, newy);
8543
8544   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8545       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8546     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8547                              MV_DIR_OPPOSITE(direction));
8548 }
8549
8550 int AmoebeNachbarNr(int ax, int ay)
8551 {
8552   int i;
8553   int element = Feld[ax][ay];
8554   int group_nr = 0;
8555   static int xy[4][2] =
8556   {
8557     { 0, -1 },
8558     { -1, 0 },
8559     { +1, 0 },
8560     { 0, +1 }
8561   };
8562
8563   for (i = 0; i < NUM_DIRECTIONS; i++)
8564   {
8565     int x = ax + xy[i][0];
8566     int y = ay + xy[i][1];
8567
8568     if (!IN_LEV_FIELD(x, y))
8569       continue;
8570
8571     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8572       group_nr = AmoebaNr[x][y];
8573   }
8574
8575   return group_nr;
8576 }
8577
8578 static void AmoebenVereinigen(int ax, int ay)
8579 {
8580   int i, x, y, xx, yy;
8581   int new_group_nr = AmoebaNr[ax][ay];
8582   static int xy[4][2] =
8583   {
8584     { 0, -1 },
8585     { -1, 0 },
8586     { +1, 0 },
8587     { 0, +1 }
8588   };
8589
8590   if (new_group_nr == 0)
8591     return;
8592
8593   for (i = 0; i < NUM_DIRECTIONS; i++)
8594   {
8595     x = ax + xy[i][0];
8596     y = ay + xy[i][1];
8597
8598     if (!IN_LEV_FIELD(x, y))
8599       continue;
8600
8601     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8602          Feld[x][y] == EL_BD_AMOEBA ||
8603          Feld[x][y] == EL_AMOEBA_DEAD) &&
8604         AmoebaNr[x][y] != new_group_nr)
8605     {
8606       int old_group_nr = AmoebaNr[x][y];
8607
8608       if (old_group_nr == 0)
8609         return;
8610
8611       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8612       AmoebaCnt[old_group_nr] = 0;
8613       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8614       AmoebaCnt2[old_group_nr] = 0;
8615
8616       SCAN_PLAYFIELD(xx, yy)
8617       {
8618         if (AmoebaNr[xx][yy] == old_group_nr)
8619           AmoebaNr[xx][yy] = new_group_nr;
8620       }
8621     }
8622   }
8623 }
8624
8625 void AmoebeUmwandeln(int ax, int ay)
8626 {
8627   int i, x, y;
8628
8629   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8630   {
8631     int group_nr = AmoebaNr[ax][ay];
8632
8633 #ifdef DEBUG
8634     if (group_nr == 0)
8635     {
8636       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8637       printf("AmoebeUmwandeln(): This should never happen!\n");
8638       return;
8639     }
8640 #endif
8641
8642     SCAN_PLAYFIELD(x, y)
8643     {
8644       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8645       {
8646         AmoebaNr[x][y] = 0;
8647         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8648       }
8649     }
8650
8651     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8652                             SND_AMOEBA_TURNING_TO_GEM :
8653                             SND_AMOEBA_TURNING_TO_ROCK));
8654     Bang(ax, ay);
8655   }
8656   else
8657   {
8658     static int xy[4][2] =
8659     {
8660       { 0, -1 },
8661       { -1, 0 },
8662       { +1, 0 },
8663       { 0, +1 }
8664     };
8665
8666     for (i = 0; i < NUM_DIRECTIONS; i++)
8667     {
8668       x = ax + xy[i][0];
8669       y = ay + xy[i][1];
8670
8671       if (!IN_LEV_FIELD(x, y))
8672         continue;
8673
8674       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8675       {
8676         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8677                               SND_AMOEBA_TURNING_TO_GEM :
8678                               SND_AMOEBA_TURNING_TO_ROCK));
8679         Bang(x, y);
8680       }
8681     }
8682   }
8683 }
8684
8685 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8686 {
8687   int x, y;
8688   int group_nr = AmoebaNr[ax][ay];
8689   boolean done = FALSE;
8690
8691 #ifdef DEBUG
8692   if (group_nr == 0)
8693   {
8694     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8695     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8696     return;
8697   }
8698 #endif
8699
8700   SCAN_PLAYFIELD(x, y)
8701   {
8702     if (AmoebaNr[x][y] == group_nr &&
8703         (Feld[x][y] == EL_AMOEBA_DEAD ||
8704          Feld[x][y] == EL_BD_AMOEBA ||
8705          Feld[x][y] == EL_AMOEBA_GROWING))
8706     {
8707       AmoebaNr[x][y] = 0;
8708       Feld[x][y] = new_element;
8709       InitField(x, y, FALSE);
8710       TEST_DrawLevelField(x, y);
8711       done = TRUE;
8712     }
8713   }
8714
8715   if (done)
8716     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8717                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8718                             SND_BD_AMOEBA_TURNING_TO_GEM));
8719 }
8720
8721 static void AmoebeWaechst(int x, int y)
8722 {
8723   static unsigned int sound_delay = 0;
8724   static unsigned int sound_delay_value = 0;
8725
8726   if (!MovDelay[x][y])          // start new growing cycle
8727   {
8728     MovDelay[x][y] = 7;
8729
8730     if (DelayReached(&sound_delay, sound_delay_value))
8731     {
8732       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8733       sound_delay_value = 30;
8734     }
8735   }
8736
8737   if (MovDelay[x][y])           // wait some time before growing bigger
8738   {
8739     MovDelay[x][y]--;
8740     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8741     {
8742       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8743                                            6 - MovDelay[x][y]);
8744
8745       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8746     }
8747
8748     if (!MovDelay[x][y])
8749     {
8750       Feld[x][y] = Store[x][y];
8751       Store[x][y] = 0;
8752       TEST_DrawLevelField(x, y);
8753     }
8754   }
8755 }
8756
8757 static void AmoebaDisappearing(int x, int y)
8758 {
8759   static unsigned int sound_delay = 0;
8760   static unsigned int sound_delay_value = 0;
8761
8762   if (!MovDelay[x][y])          // start new shrinking cycle
8763   {
8764     MovDelay[x][y] = 7;
8765
8766     if (DelayReached(&sound_delay, sound_delay_value))
8767       sound_delay_value = 30;
8768   }
8769
8770   if (MovDelay[x][y])           // wait some time before shrinking
8771   {
8772     MovDelay[x][y]--;
8773     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8774     {
8775       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8776                                            6 - MovDelay[x][y]);
8777
8778       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8779     }
8780
8781     if (!MovDelay[x][y])
8782     {
8783       Feld[x][y] = EL_EMPTY;
8784       TEST_DrawLevelField(x, y);
8785
8786       // don't let mole enter this field in this cycle;
8787       // (give priority to objects falling to this field from above)
8788       Stop[x][y] = TRUE;
8789     }
8790   }
8791 }
8792
8793 static void AmoebeAbleger(int ax, int ay)
8794 {
8795   int i;
8796   int element = Feld[ax][ay];
8797   int graphic = el2img(element);
8798   int newax = ax, neway = ay;
8799   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8800   static int xy[4][2] =
8801   {
8802     { 0, -1 },
8803     { -1, 0 },
8804     { +1, 0 },
8805     { 0, +1 }
8806   };
8807
8808   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8809   {
8810     Feld[ax][ay] = EL_AMOEBA_DEAD;
8811     TEST_DrawLevelField(ax, ay);
8812     return;
8813   }
8814
8815   if (IS_ANIMATED(graphic))
8816     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8817
8818   if (!MovDelay[ax][ay])        // start making new amoeba field
8819     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8820
8821   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8822   {
8823     MovDelay[ax][ay]--;
8824     if (MovDelay[ax][ay])
8825       return;
8826   }
8827
8828   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8829   {
8830     int start = RND(4);
8831     int x = ax + xy[start][0];
8832     int y = ay + xy[start][1];
8833
8834     if (!IN_LEV_FIELD(x, y))
8835       return;
8836
8837     if (IS_FREE(x, y) ||
8838         CAN_GROW_INTO(Feld[x][y]) ||
8839         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8840         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8841     {
8842       newax = x;
8843       neway = y;
8844     }
8845
8846     if (newax == ax && neway == ay)
8847       return;
8848   }
8849   else                          // normal or "filled" (BD style) amoeba
8850   {
8851     int start = RND(4);
8852     boolean waiting_for_player = FALSE;
8853
8854     for (i = 0; i < NUM_DIRECTIONS; i++)
8855     {
8856       int j = (start + i) % 4;
8857       int x = ax + xy[j][0];
8858       int y = ay + xy[j][1];
8859
8860       if (!IN_LEV_FIELD(x, y))
8861         continue;
8862
8863       if (IS_FREE(x, y) ||
8864           CAN_GROW_INTO(Feld[x][y]) ||
8865           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8866           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8867       {
8868         newax = x;
8869         neway = y;
8870         break;
8871       }
8872       else if (IS_PLAYER(x, y))
8873         waiting_for_player = TRUE;
8874     }
8875
8876     if (newax == ax && neway == ay)             // amoeba cannot grow
8877     {
8878       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8879       {
8880         Feld[ax][ay] = EL_AMOEBA_DEAD;
8881         TEST_DrawLevelField(ax, ay);
8882         AmoebaCnt[AmoebaNr[ax][ay]]--;
8883
8884         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8885         {
8886           if (element == EL_AMOEBA_FULL)
8887             AmoebeUmwandeln(ax, ay);
8888           else if (element == EL_BD_AMOEBA)
8889             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8890         }
8891       }
8892       return;
8893     }
8894     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8895     {
8896       // amoeba gets larger by growing in some direction
8897
8898       int new_group_nr = AmoebaNr[ax][ay];
8899
8900 #ifdef DEBUG
8901   if (new_group_nr == 0)
8902   {
8903     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8904     printf("AmoebeAbleger(): This should never happen!\n");
8905     return;
8906   }
8907 #endif
8908
8909       AmoebaNr[newax][neway] = new_group_nr;
8910       AmoebaCnt[new_group_nr]++;
8911       AmoebaCnt2[new_group_nr]++;
8912
8913       // if amoeba touches other amoeba(s) after growing, unify them
8914       AmoebenVereinigen(newax, neway);
8915
8916       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8917       {
8918         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8919         return;
8920       }
8921     }
8922   }
8923
8924   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8925       (neway == lev_fieldy - 1 && newax != ax))
8926   {
8927     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8928     Store[newax][neway] = element;
8929   }
8930   else if (neway == ay || element == EL_EMC_DRIPPER)
8931   {
8932     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8933
8934     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8935   }
8936   else
8937   {
8938     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8939     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8940     Store[ax][ay] = EL_AMOEBA_DROP;
8941     ContinueMoving(ax, ay);
8942     return;
8943   }
8944
8945   TEST_DrawLevelField(newax, neway);
8946 }
8947
8948 static void Life(int ax, int ay)
8949 {
8950   int x1, y1, x2, y2;
8951   int life_time = 40;
8952   int element = Feld[ax][ay];
8953   int graphic = el2img(element);
8954   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8955                          level.biomaze);
8956   boolean changed = FALSE;
8957
8958   if (IS_ANIMATED(graphic))
8959     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8960
8961   if (Stop[ax][ay])
8962     return;
8963
8964   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8965     MovDelay[ax][ay] = life_time;
8966
8967   if (MovDelay[ax][ay])         // wait some time before next cycle
8968   {
8969     MovDelay[ax][ay]--;
8970     if (MovDelay[ax][ay])
8971       return;
8972   }
8973
8974   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8975   {
8976     int xx = ax+x1, yy = ay+y1;
8977     int old_element = Feld[xx][yy];
8978     int num_neighbours = 0;
8979
8980     if (!IN_LEV_FIELD(xx, yy))
8981       continue;
8982
8983     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8984     {
8985       int x = xx+x2, y = yy+y2;
8986
8987       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8988         continue;
8989
8990       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8991       boolean is_neighbour = FALSE;
8992
8993       if (level.use_life_bugs)
8994         is_neighbour =
8995           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8996            (IS_FREE(x, y)                             &&  Stop[x][y]));
8997       else
8998         is_neighbour =
8999           (Last[x][y] == element || is_player_cell);
9000
9001       if (is_neighbour)
9002         num_neighbours++;
9003     }
9004
9005     boolean is_free = FALSE;
9006
9007     if (level.use_life_bugs)
9008       is_free = (IS_FREE(xx, yy));
9009     else
9010       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9011
9012     if (xx == ax && yy == ay)           // field in the middle
9013     {
9014       if (num_neighbours < life_parameter[0] ||
9015           num_neighbours > life_parameter[1])
9016       {
9017         Feld[xx][yy] = EL_EMPTY;
9018         if (Feld[xx][yy] != old_element)
9019           TEST_DrawLevelField(xx, yy);
9020         Stop[xx][yy] = TRUE;
9021         changed = TRUE;
9022       }
9023     }
9024     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9025     {                                   // free border field
9026       if (num_neighbours >= life_parameter[2] &&
9027           num_neighbours <= life_parameter[3])
9028       {
9029         Feld[xx][yy] = element;
9030         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9031         if (Feld[xx][yy] != old_element)
9032           TEST_DrawLevelField(xx, yy);
9033         Stop[xx][yy] = TRUE;
9034         changed = TRUE;
9035       }
9036     }
9037   }
9038
9039   if (changed)
9040     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9041                    SND_GAME_OF_LIFE_GROWING);
9042 }
9043
9044 static void InitRobotWheel(int x, int y)
9045 {
9046   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9047 }
9048
9049 static void RunRobotWheel(int x, int y)
9050 {
9051   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9052 }
9053
9054 static void StopRobotWheel(int x, int y)
9055 {
9056   if (game.robot_wheel_x == x &&
9057       game.robot_wheel_y == y)
9058   {
9059     game.robot_wheel_x = -1;
9060     game.robot_wheel_y = -1;
9061     game.robot_wheel_active = FALSE;
9062   }
9063 }
9064
9065 static void InitTimegateWheel(int x, int y)
9066 {
9067   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9068 }
9069
9070 static void RunTimegateWheel(int x, int y)
9071 {
9072   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9073 }
9074
9075 static void InitMagicBallDelay(int x, int y)
9076 {
9077   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9078 }
9079
9080 static void ActivateMagicBall(int bx, int by)
9081 {
9082   int x, y;
9083
9084   if (level.ball_random)
9085   {
9086     int pos_border = RND(8);    // select one of the eight border elements
9087     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9088     int xx = pos_content % 3;
9089     int yy = pos_content / 3;
9090
9091     x = bx - 1 + xx;
9092     y = by - 1 + yy;
9093
9094     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9095       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9096   }
9097   else
9098   {
9099     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9100     {
9101       int xx = x - bx + 1;
9102       int yy = y - by + 1;
9103
9104       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9105         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9106     }
9107   }
9108
9109   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9110 }
9111
9112 static void CheckExit(int x, int y)
9113 {
9114   if (game.gems_still_needed > 0 ||
9115       game.sokoban_fields_still_needed > 0 ||
9116       game.sokoban_objects_still_needed > 0 ||
9117       game.lights_still_needed > 0)
9118   {
9119     int element = Feld[x][y];
9120     int graphic = el2img(element);
9121
9122     if (IS_ANIMATED(graphic))
9123       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9124
9125     return;
9126   }
9127
9128   // do not re-open exit door closed after last player
9129   if (game.all_players_gone)
9130     return;
9131
9132   Feld[x][y] = EL_EXIT_OPENING;
9133
9134   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9135 }
9136
9137 static void CheckExitEM(int x, int y)
9138 {
9139   if (game.gems_still_needed > 0 ||
9140       game.sokoban_fields_still_needed > 0 ||
9141       game.sokoban_objects_still_needed > 0 ||
9142       game.lights_still_needed > 0)
9143   {
9144     int element = Feld[x][y];
9145     int graphic = el2img(element);
9146
9147     if (IS_ANIMATED(graphic))
9148       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9149
9150     return;
9151   }
9152
9153   // do not re-open exit door closed after last player
9154   if (game.all_players_gone)
9155     return;
9156
9157   Feld[x][y] = EL_EM_EXIT_OPENING;
9158
9159   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9160 }
9161
9162 static void CheckExitSteel(int x, int y)
9163 {
9164   if (game.gems_still_needed > 0 ||
9165       game.sokoban_fields_still_needed > 0 ||
9166       game.sokoban_objects_still_needed > 0 ||
9167       game.lights_still_needed > 0)
9168   {
9169     int element = Feld[x][y];
9170     int graphic = el2img(element);
9171
9172     if (IS_ANIMATED(graphic))
9173       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9174
9175     return;
9176   }
9177
9178   // do not re-open exit door closed after last player
9179   if (game.all_players_gone)
9180     return;
9181
9182   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9183
9184   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9185 }
9186
9187 static void CheckExitSteelEM(int x, int y)
9188 {
9189   if (game.gems_still_needed > 0 ||
9190       game.sokoban_fields_still_needed > 0 ||
9191       game.sokoban_objects_still_needed > 0 ||
9192       game.lights_still_needed > 0)
9193   {
9194     int element = Feld[x][y];
9195     int graphic = el2img(element);
9196
9197     if (IS_ANIMATED(graphic))
9198       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9199
9200     return;
9201   }
9202
9203   // do not re-open exit door closed after last player
9204   if (game.all_players_gone)
9205     return;
9206
9207   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9208
9209   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9210 }
9211
9212 static void CheckExitSP(int x, int y)
9213 {
9214   if (game.gems_still_needed > 0)
9215   {
9216     int element = Feld[x][y];
9217     int graphic = el2img(element);
9218
9219     if (IS_ANIMATED(graphic))
9220       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9221
9222     return;
9223   }
9224
9225   // do not re-open exit door closed after last player
9226   if (game.all_players_gone)
9227     return;
9228
9229   Feld[x][y] = EL_SP_EXIT_OPENING;
9230
9231   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9232 }
9233
9234 static void CloseAllOpenTimegates(void)
9235 {
9236   int x, y;
9237
9238   SCAN_PLAYFIELD(x, y)
9239   {
9240     int element = Feld[x][y];
9241
9242     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9243     {
9244       Feld[x][y] = EL_TIMEGATE_CLOSING;
9245
9246       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9247     }
9248   }
9249 }
9250
9251 static void DrawTwinkleOnField(int x, int y)
9252 {
9253   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9254     return;
9255
9256   if (Feld[x][y] == EL_BD_DIAMOND)
9257     return;
9258
9259   if (MovDelay[x][y] == 0)      // next animation frame
9260     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9261
9262   if (MovDelay[x][y] != 0)      // wait some time before next frame
9263   {
9264     MovDelay[x][y]--;
9265
9266     DrawLevelElementAnimation(x, y, Feld[x][y]);
9267
9268     if (MovDelay[x][y] != 0)
9269     {
9270       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9271                                            10 - MovDelay[x][y]);
9272
9273       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9274     }
9275   }
9276 }
9277
9278 static void MauerWaechst(int x, int y)
9279 {
9280   int delay = 6;
9281
9282   if (!MovDelay[x][y])          // next animation frame
9283     MovDelay[x][y] = 3 * delay;
9284
9285   if (MovDelay[x][y])           // wait some time before next frame
9286   {
9287     MovDelay[x][y]--;
9288
9289     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9290     {
9291       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9292       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9293
9294       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9295     }
9296
9297     if (!MovDelay[x][y])
9298     {
9299       if (MovDir[x][y] == MV_LEFT)
9300       {
9301         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9302           TEST_DrawLevelField(x - 1, y);
9303       }
9304       else if (MovDir[x][y] == MV_RIGHT)
9305       {
9306         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9307           TEST_DrawLevelField(x + 1, y);
9308       }
9309       else if (MovDir[x][y] == MV_UP)
9310       {
9311         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9312           TEST_DrawLevelField(x, y - 1);
9313       }
9314       else
9315       {
9316         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9317           TEST_DrawLevelField(x, y + 1);
9318       }
9319
9320       Feld[x][y] = Store[x][y];
9321       Store[x][y] = 0;
9322       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9323       TEST_DrawLevelField(x, y);
9324     }
9325   }
9326 }
9327
9328 static void MauerAbleger(int ax, int ay)
9329 {
9330   int element = Feld[ax][ay];
9331   int graphic = el2img(element);
9332   boolean oben_frei = FALSE, unten_frei = FALSE;
9333   boolean links_frei = FALSE, rechts_frei = FALSE;
9334   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9335   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9336   boolean new_wall = FALSE;
9337
9338   if (IS_ANIMATED(graphic))
9339     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9340
9341   if (!MovDelay[ax][ay])        // start building new wall
9342     MovDelay[ax][ay] = 6;
9343
9344   if (MovDelay[ax][ay])         // wait some time before building new wall
9345   {
9346     MovDelay[ax][ay]--;
9347     if (MovDelay[ax][ay])
9348       return;
9349   }
9350
9351   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9352     oben_frei = TRUE;
9353   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9354     unten_frei = TRUE;
9355   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9356     links_frei = TRUE;
9357   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9358     rechts_frei = TRUE;
9359
9360   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9361       element == EL_EXPANDABLE_WALL_ANY)
9362   {
9363     if (oben_frei)
9364     {
9365       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9366       Store[ax][ay-1] = element;
9367       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9368       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9369         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9370                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9371       new_wall = TRUE;
9372     }
9373     if (unten_frei)
9374     {
9375       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9376       Store[ax][ay+1] = element;
9377       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9378       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9379         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9380                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9381       new_wall = TRUE;
9382     }
9383   }
9384
9385   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9386       element == EL_EXPANDABLE_WALL_ANY ||
9387       element == EL_EXPANDABLE_WALL ||
9388       element == EL_BD_EXPANDABLE_WALL)
9389   {
9390     if (links_frei)
9391     {
9392       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9393       Store[ax-1][ay] = element;
9394       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9395       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9396         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9397                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9398       new_wall = TRUE;
9399     }
9400
9401     if (rechts_frei)
9402     {
9403       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9404       Store[ax+1][ay] = element;
9405       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9406       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9407         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9408                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9409       new_wall = TRUE;
9410     }
9411   }
9412
9413   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9414     TEST_DrawLevelField(ax, ay);
9415
9416   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9417     oben_massiv = TRUE;
9418   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9419     unten_massiv = TRUE;
9420   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9421     links_massiv = TRUE;
9422   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9423     rechts_massiv = TRUE;
9424
9425   if (((oben_massiv && unten_massiv) ||
9426        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9427        element == EL_EXPANDABLE_WALL) &&
9428       ((links_massiv && rechts_massiv) ||
9429        element == EL_EXPANDABLE_WALL_VERTICAL))
9430     Feld[ax][ay] = EL_WALL;
9431
9432   if (new_wall)
9433     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9434 }
9435
9436 static void MauerAblegerStahl(int ax, int ay)
9437 {
9438   int element = Feld[ax][ay];
9439   int graphic = el2img(element);
9440   boolean oben_frei = FALSE, unten_frei = FALSE;
9441   boolean links_frei = FALSE, rechts_frei = FALSE;
9442   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9443   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9444   boolean new_wall = FALSE;
9445
9446   if (IS_ANIMATED(graphic))
9447     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9448
9449   if (!MovDelay[ax][ay])        // start building new wall
9450     MovDelay[ax][ay] = 6;
9451
9452   if (MovDelay[ax][ay])         // wait some time before building new wall
9453   {
9454     MovDelay[ax][ay]--;
9455     if (MovDelay[ax][ay])
9456       return;
9457   }
9458
9459   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9460     oben_frei = TRUE;
9461   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9462     unten_frei = TRUE;
9463   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9464     links_frei = TRUE;
9465   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9466     rechts_frei = TRUE;
9467
9468   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9469       element == EL_EXPANDABLE_STEELWALL_ANY)
9470   {
9471     if (oben_frei)
9472     {
9473       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9474       Store[ax][ay-1] = element;
9475       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9476       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9477         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9478                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9479       new_wall = TRUE;
9480     }
9481     if (unten_frei)
9482     {
9483       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9484       Store[ax][ay+1] = element;
9485       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9486       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9487         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9488                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9489       new_wall = TRUE;
9490     }
9491   }
9492
9493   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9494       element == EL_EXPANDABLE_STEELWALL_ANY)
9495   {
9496     if (links_frei)
9497     {
9498       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9499       Store[ax-1][ay] = element;
9500       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9501       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9502         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9503                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9504       new_wall = TRUE;
9505     }
9506
9507     if (rechts_frei)
9508     {
9509       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9510       Store[ax+1][ay] = element;
9511       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9512       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9513         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9514                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9515       new_wall = TRUE;
9516     }
9517   }
9518
9519   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9520     oben_massiv = TRUE;
9521   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9522     unten_massiv = TRUE;
9523   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9524     links_massiv = TRUE;
9525   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9526     rechts_massiv = TRUE;
9527
9528   if (((oben_massiv && unten_massiv) ||
9529        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9530       ((links_massiv && rechts_massiv) ||
9531        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9532     Feld[ax][ay] = EL_STEELWALL;
9533
9534   if (new_wall)
9535     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9536 }
9537
9538 static void CheckForDragon(int x, int y)
9539 {
9540   int i, j;
9541   boolean dragon_found = FALSE;
9542   static int xy[4][2] =
9543   {
9544     { 0, -1 },
9545     { -1, 0 },
9546     { +1, 0 },
9547     { 0, +1 }
9548   };
9549
9550   for (i = 0; i < NUM_DIRECTIONS; i++)
9551   {
9552     for (j = 0; j < 4; j++)
9553     {
9554       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9555
9556       if (IN_LEV_FIELD(xx, yy) &&
9557           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9558       {
9559         if (Feld[xx][yy] == EL_DRAGON)
9560           dragon_found = TRUE;
9561       }
9562       else
9563         break;
9564     }
9565   }
9566
9567   if (!dragon_found)
9568   {
9569     for (i = 0; i < NUM_DIRECTIONS; i++)
9570     {
9571       for (j = 0; j < 3; j++)
9572       {
9573         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9574   
9575         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9576         {
9577           Feld[xx][yy] = EL_EMPTY;
9578           TEST_DrawLevelField(xx, yy);
9579         }
9580         else
9581           break;
9582       }
9583     }
9584   }
9585 }
9586
9587 static void InitBuggyBase(int x, int y)
9588 {
9589   int element = Feld[x][y];
9590   int activating_delay = FRAMES_PER_SECOND / 4;
9591
9592   ChangeDelay[x][y] =
9593     (element == EL_SP_BUGGY_BASE ?
9594      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9595      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9596      activating_delay :
9597      element == EL_SP_BUGGY_BASE_ACTIVE ?
9598      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9599 }
9600
9601 static void WarnBuggyBase(int x, int y)
9602 {
9603   int i;
9604   static int xy[4][2] =
9605   {
9606     { 0, -1 },
9607     { -1, 0 },
9608     { +1, 0 },
9609     { 0, +1 }
9610   };
9611
9612   for (i = 0; i < NUM_DIRECTIONS; i++)
9613   {
9614     int xx = x + xy[i][0];
9615     int yy = y + xy[i][1];
9616
9617     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9618     {
9619       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9620
9621       break;
9622     }
9623   }
9624 }
9625
9626 static void InitTrap(int x, int y)
9627 {
9628   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9629 }
9630
9631 static void ActivateTrap(int x, int y)
9632 {
9633   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9634 }
9635
9636 static void ChangeActiveTrap(int x, int y)
9637 {
9638   int graphic = IMG_TRAP_ACTIVE;
9639
9640   // if new animation frame was drawn, correct crumbled sand border
9641   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9642     TEST_DrawLevelFieldCrumbled(x, y);
9643 }
9644
9645 static int getSpecialActionElement(int element, int number, int base_element)
9646 {
9647   return (element != EL_EMPTY ? element :
9648           number != -1 ? base_element + number - 1 :
9649           EL_EMPTY);
9650 }
9651
9652 static int getModifiedActionNumber(int value_old, int operator, int operand,
9653                                    int value_min, int value_max)
9654 {
9655   int value_new = (operator == CA_MODE_SET      ? operand :
9656                    operator == CA_MODE_ADD      ? value_old + operand :
9657                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9658                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9659                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9660                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9661                    value_old);
9662
9663   return (value_new < value_min ? value_min :
9664           value_new > value_max ? value_max :
9665           value_new);
9666 }
9667
9668 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9669 {
9670   struct ElementInfo *ei = &element_info[element];
9671   struct ElementChangeInfo *change = &ei->change_page[page];
9672   int target_element = change->target_element;
9673   int action_type = change->action_type;
9674   int action_mode = change->action_mode;
9675   int action_arg = change->action_arg;
9676   int action_element = change->action_element;
9677   int i;
9678
9679   if (!change->has_action)
9680     return;
9681
9682   // ---------- determine action paramater values -----------------------------
9683
9684   int level_time_value =
9685     (level.time > 0 ? TimeLeft :
9686      TimePlayed);
9687
9688   int action_arg_element_raw =
9689     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9690      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9691      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9692      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9693      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9694      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9695      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9696      EL_EMPTY);
9697   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9698
9699   int action_arg_direction =
9700     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9701      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9702      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9703      change->actual_trigger_side :
9704      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9705      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9706      MV_NONE);
9707
9708   int action_arg_number_min =
9709     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9710      CA_ARG_MIN);
9711
9712   int action_arg_number_max =
9713     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9714      action_type == CA_SET_LEVEL_GEMS ? 999 :
9715      action_type == CA_SET_LEVEL_TIME ? 9999 :
9716      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9717      action_type == CA_SET_CE_VALUE ? 9999 :
9718      action_type == CA_SET_CE_SCORE ? 9999 :
9719      CA_ARG_MAX);
9720
9721   int action_arg_number_reset =
9722     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9723      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9724      action_type == CA_SET_LEVEL_TIME ? level.time :
9725      action_type == CA_SET_LEVEL_SCORE ? 0 :
9726      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9727      action_type == CA_SET_CE_SCORE ? 0 :
9728      0);
9729
9730   int action_arg_number =
9731     (action_arg <= CA_ARG_MAX ? action_arg :
9732      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9733      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9734      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9735      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9736      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9737      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9738      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9739      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9740      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9741      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9742      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9743      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9744      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9745      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9746      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9747      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9748      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9749      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9750      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9751      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9752      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9753      -1);
9754
9755   int action_arg_number_old =
9756     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9757      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9758      action_type == CA_SET_LEVEL_SCORE ? game.score :
9759      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9760      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9761      0);
9762
9763   int action_arg_number_new =
9764     getModifiedActionNumber(action_arg_number_old,
9765                             action_mode, action_arg_number,
9766                             action_arg_number_min, action_arg_number_max);
9767
9768   int trigger_player_bits =
9769     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9770      change->actual_trigger_player_bits : change->trigger_player);
9771
9772   int action_arg_player_bits =
9773     (action_arg >= CA_ARG_PLAYER_1 &&
9774      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9775      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9776      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9777      PLAYER_BITS_ANY);
9778
9779   // ---------- execute action  -----------------------------------------------
9780
9781   switch (action_type)
9782   {
9783     case CA_NO_ACTION:
9784     {
9785       return;
9786     }
9787
9788     // ---------- level actions  ----------------------------------------------
9789
9790     case CA_RESTART_LEVEL:
9791     {
9792       game.restart_level = TRUE;
9793
9794       break;
9795     }
9796
9797     case CA_SHOW_ENVELOPE:
9798     {
9799       int element = getSpecialActionElement(action_arg_element,
9800                                             action_arg_number, EL_ENVELOPE_1);
9801
9802       if (IS_ENVELOPE(element))
9803         local_player->show_envelope = element;
9804
9805       break;
9806     }
9807
9808     case CA_SET_LEVEL_TIME:
9809     {
9810       if (level.time > 0)       // only modify limited time value
9811       {
9812         TimeLeft = action_arg_number_new;
9813
9814         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9815
9816         DisplayGameControlValues();
9817
9818         if (!TimeLeft && setup.time_limit)
9819           for (i = 0; i < MAX_PLAYERS; i++)
9820             KillPlayer(&stored_player[i]);
9821       }
9822
9823       break;
9824     }
9825
9826     case CA_SET_LEVEL_SCORE:
9827     {
9828       game.score = action_arg_number_new;
9829
9830       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9831
9832       DisplayGameControlValues();
9833
9834       break;
9835     }
9836
9837     case CA_SET_LEVEL_GEMS:
9838     {
9839       game.gems_still_needed = action_arg_number_new;
9840
9841       game.snapshot.collected_item = TRUE;
9842
9843       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9844
9845       DisplayGameControlValues();
9846
9847       break;
9848     }
9849
9850     case CA_SET_LEVEL_WIND:
9851     {
9852       game.wind_direction = action_arg_direction;
9853
9854       break;
9855     }
9856
9857     case CA_SET_LEVEL_RANDOM_SEED:
9858     {
9859       // ensure that setting a new random seed while playing is predictable
9860       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9861
9862       break;
9863     }
9864
9865     // ---------- player actions  ---------------------------------------------
9866
9867     case CA_MOVE_PLAYER:
9868     {
9869       // automatically move to the next field in specified direction
9870       for (i = 0; i < MAX_PLAYERS; i++)
9871         if (trigger_player_bits & (1 << i))
9872           stored_player[i].programmed_action = action_arg_direction;
9873
9874       break;
9875     }
9876
9877     case CA_EXIT_PLAYER:
9878     {
9879       for (i = 0; i < MAX_PLAYERS; i++)
9880         if (action_arg_player_bits & (1 << i))
9881           ExitPlayer(&stored_player[i]);
9882
9883       if (game.players_still_needed == 0)
9884         LevelSolved();
9885
9886       break;
9887     }
9888
9889     case CA_KILL_PLAYER:
9890     {
9891       for (i = 0; i < MAX_PLAYERS; i++)
9892         if (action_arg_player_bits & (1 << i))
9893           KillPlayer(&stored_player[i]);
9894
9895       break;
9896     }
9897
9898     case CA_SET_PLAYER_KEYS:
9899     {
9900       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9901       int element = getSpecialActionElement(action_arg_element,
9902                                             action_arg_number, EL_KEY_1);
9903
9904       if (IS_KEY(element))
9905       {
9906         for (i = 0; i < MAX_PLAYERS; i++)
9907         {
9908           if (trigger_player_bits & (1 << i))
9909           {
9910             stored_player[i].key[KEY_NR(element)] = key_state;
9911
9912             DrawGameDoorValues();
9913           }
9914         }
9915       }
9916
9917       break;
9918     }
9919
9920     case CA_SET_PLAYER_SPEED:
9921     {
9922       for (i = 0; i < MAX_PLAYERS; i++)
9923       {
9924         if (trigger_player_bits & (1 << i))
9925         {
9926           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9927
9928           if (action_arg == CA_ARG_SPEED_FASTER &&
9929               stored_player[i].cannot_move)
9930           {
9931             action_arg_number = STEPSIZE_VERY_SLOW;
9932           }
9933           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9934                    action_arg == CA_ARG_SPEED_FASTER)
9935           {
9936             action_arg_number = 2;
9937             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9938                            CA_MODE_MULTIPLY);
9939           }
9940           else if (action_arg == CA_ARG_NUMBER_RESET)
9941           {
9942             action_arg_number = level.initial_player_stepsize[i];
9943           }
9944
9945           move_stepsize =
9946             getModifiedActionNumber(move_stepsize,
9947                                     action_mode,
9948                                     action_arg_number,
9949                                     action_arg_number_min,
9950                                     action_arg_number_max);
9951
9952           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9953         }
9954       }
9955
9956       break;
9957     }
9958
9959     case CA_SET_PLAYER_SHIELD:
9960     {
9961       for (i = 0; i < MAX_PLAYERS; i++)
9962       {
9963         if (trigger_player_bits & (1 << i))
9964         {
9965           if (action_arg == CA_ARG_SHIELD_OFF)
9966           {
9967             stored_player[i].shield_normal_time_left = 0;
9968             stored_player[i].shield_deadly_time_left = 0;
9969           }
9970           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9971           {
9972             stored_player[i].shield_normal_time_left = 999999;
9973           }
9974           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9975           {
9976             stored_player[i].shield_normal_time_left = 999999;
9977             stored_player[i].shield_deadly_time_left = 999999;
9978           }
9979         }
9980       }
9981
9982       break;
9983     }
9984
9985     case CA_SET_PLAYER_GRAVITY:
9986     {
9987       for (i = 0; i < MAX_PLAYERS; i++)
9988       {
9989         if (trigger_player_bits & (1 << i))
9990         {
9991           stored_player[i].gravity =
9992             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9993              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9994              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9995              stored_player[i].gravity);
9996         }
9997       }
9998
9999       break;
10000     }
10001
10002     case CA_SET_PLAYER_ARTWORK:
10003     {
10004       for (i = 0; i < MAX_PLAYERS; i++)
10005       {
10006         if (trigger_player_bits & (1 << i))
10007         {
10008           int artwork_element = action_arg_element;
10009
10010           if (action_arg == CA_ARG_ELEMENT_RESET)
10011             artwork_element =
10012               (level.use_artwork_element[i] ? level.artwork_element[i] :
10013                stored_player[i].element_nr);
10014
10015           if (stored_player[i].artwork_element != artwork_element)
10016             stored_player[i].Frame = 0;
10017
10018           stored_player[i].artwork_element = artwork_element;
10019
10020           SetPlayerWaiting(&stored_player[i], FALSE);
10021
10022           // set number of special actions for bored and sleeping animation
10023           stored_player[i].num_special_action_bored =
10024             get_num_special_action(artwork_element,
10025                                    ACTION_BORING_1, ACTION_BORING_LAST);
10026           stored_player[i].num_special_action_sleeping =
10027             get_num_special_action(artwork_element,
10028                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10029         }
10030       }
10031
10032       break;
10033     }
10034
10035     case CA_SET_PLAYER_INVENTORY:
10036     {
10037       for (i = 0; i < MAX_PLAYERS; i++)
10038       {
10039         struct PlayerInfo *player = &stored_player[i];
10040         int j, k;
10041
10042         if (trigger_player_bits & (1 << i))
10043         {
10044           int inventory_element = action_arg_element;
10045
10046           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10047               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10048               action_arg == CA_ARG_ELEMENT_ACTION)
10049           {
10050             int element = inventory_element;
10051             int collect_count = element_info[element].collect_count_initial;
10052
10053             if (!IS_CUSTOM_ELEMENT(element))
10054               collect_count = 1;
10055
10056             if (collect_count == 0)
10057               player->inventory_infinite_element = element;
10058             else
10059               for (k = 0; k < collect_count; k++)
10060                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10061                   player->inventory_element[player->inventory_size++] =
10062                     element;
10063           }
10064           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10065                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10066                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10067           {
10068             if (player->inventory_infinite_element != EL_UNDEFINED &&
10069                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10070                                      action_arg_element_raw))
10071               player->inventory_infinite_element = EL_UNDEFINED;
10072
10073             for (k = 0, j = 0; j < player->inventory_size; j++)
10074             {
10075               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10076                                         action_arg_element_raw))
10077                 player->inventory_element[k++] = player->inventory_element[j];
10078             }
10079
10080             player->inventory_size = k;
10081           }
10082           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10083           {
10084             if (player->inventory_size > 0)
10085             {
10086               for (j = 0; j < player->inventory_size - 1; j++)
10087                 player->inventory_element[j] = player->inventory_element[j + 1];
10088
10089               player->inventory_size--;
10090             }
10091           }
10092           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10093           {
10094             if (player->inventory_size > 0)
10095               player->inventory_size--;
10096           }
10097           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10098           {
10099             player->inventory_infinite_element = EL_UNDEFINED;
10100             player->inventory_size = 0;
10101           }
10102           else if (action_arg == CA_ARG_INVENTORY_RESET)
10103           {
10104             player->inventory_infinite_element = EL_UNDEFINED;
10105             player->inventory_size = 0;
10106
10107             if (level.use_initial_inventory[i])
10108             {
10109               for (j = 0; j < level.initial_inventory_size[i]; j++)
10110               {
10111                 int element = level.initial_inventory_content[i][j];
10112                 int collect_count = element_info[element].collect_count_initial;
10113
10114                 if (!IS_CUSTOM_ELEMENT(element))
10115                   collect_count = 1;
10116
10117                 if (collect_count == 0)
10118                   player->inventory_infinite_element = element;
10119                 else
10120                   for (k = 0; k < collect_count; k++)
10121                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10122                       player->inventory_element[player->inventory_size++] =
10123                         element;
10124               }
10125             }
10126           }
10127         }
10128       }
10129
10130       break;
10131     }
10132
10133     // ---------- CE actions  -------------------------------------------------
10134
10135     case CA_SET_CE_VALUE:
10136     {
10137       int last_ce_value = CustomValue[x][y];
10138
10139       CustomValue[x][y] = action_arg_number_new;
10140
10141       if (CustomValue[x][y] != last_ce_value)
10142       {
10143         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10144         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10145
10146         if (CustomValue[x][y] == 0)
10147         {
10148           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10149           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10150         }
10151       }
10152
10153       break;
10154     }
10155
10156     case CA_SET_CE_SCORE:
10157     {
10158       int last_ce_score = ei->collect_score;
10159
10160       ei->collect_score = action_arg_number_new;
10161
10162       if (ei->collect_score != last_ce_score)
10163       {
10164         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10165         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10166
10167         if (ei->collect_score == 0)
10168         {
10169           int xx, yy;
10170
10171           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10172           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10173
10174           /*
10175             This is a very special case that seems to be a mixture between
10176             CheckElementChange() and CheckTriggeredElementChange(): while
10177             the first one only affects single elements that are triggered
10178             directly, the second one affects multiple elements in the playfield
10179             that are triggered indirectly by another element. This is a third
10180             case: Changing the CE score always affects multiple identical CEs,
10181             so every affected CE must be checked, not only the single CE for
10182             which the CE score was changed in the first place (as every instance
10183             of that CE shares the same CE score, and therefore also can change)!
10184           */
10185           SCAN_PLAYFIELD(xx, yy)
10186           {
10187             if (Feld[xx][yy] == element)
10188               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10189                                  CE_SCORE_GETS_ZERO);
10190           }
10191         }
10192       }
10193
10194       break;
10195     }
10196
10197     case CA_SET_CE_ARTWORK:
10198     {
10199       int artwork_element = action_arg_element;
10200       boolean reset_frame = FALSE;
10201       int xx, yy;
10202
10203       if (action_arg == CA_ARG_ELEMENT_RESET)
10204         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10205                            element);
10206
10207       if (ei->gfx_element != artwork_element)
10208         reset_frame = TRUE;
10209
10210       ei->gfx_element = artwork_element;
10211
10212       SCAN_PLAYFIELD(xx, yy)
10213       {
10214         if (Feld[xx][yy] == element)
10215         {
10216           if (reset_frame)
10217           {
10218             ResetGfxAnimation(xx, yy);
10219             ResetRandomAnimationValue(xx, yy);
10220           }
10221
10222           TEST_DrawLevelField(xx, yy);
10223         }
10224       }
10225
10226       break;
10227     }
10228
10229     // ---------- engine actions  ---------------------------------------------
10230
10231     case CA_SET_ENGINE_SCAN_MODE:
10232     {
10233       InitPlayfieldScanMode(action_arg);
10234
10235       break;
10236     }
10237
10238     default:
10239       break;
10240   }
10241 }
10242
10243 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10244 {
10245   int old_element = Feld[x][y];
10246   int new_element = GetElementFromGroupElement(element);
10247   int previous_move_direction = MovDir[x][y];
10248   int last_ce_value = CustomValue[x][y];
10249   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10250   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10251   boolean add_player_onto_element = (new_element_is_player &&
10252                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10253                                      IS_WALKABLE(old_element));
10254
10255   if (!add_player_onto_element)
10256   {
10257     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10258       RemoveMovingField(x, y);
10259     else
10260       RemoveField(x, y);
10261
10262     Feld[x][y] = new_element;
10263
10264     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10265       MovDir[x][y] = previous_move_direction;
10266
10267     if (element_info[new_element].use_last_ce_value)
10268       CustomValue[x][y] = last_ce_value;
10269
10270     InitField_WithBug1(x, y, FALSE);
10271
10272     new_element = Feld[x][y];   // element may have changed
10273
10274     ResetGfxAnimation(x, y);
10275     ResetRandomAnimationValue(x, y);
10276
10277     TEST_DrawLevelField(x, y);
10278
10279     if (GFX_CRUMBLED(new_element))
10280       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10281   }
10282
10283   // check if element under the player changes from accessible to unaccessible
10284   // (needed for special case of dropping element which then changes)
10285   // (must be checked after creating new element for walkable group elements)
10286   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10287       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10288   {
10289     Bang(x, y);
10290
10291     return;
10292   }
10293
10294   // "ChangeCount" not set yet to allow "entered by player" change one time
10295   if (new_element_is_player)
10296     RelocatePlayer(x, y, new_element);
10297
10298   if (is_change)
10299     ChangeCount[x][y]++;        // count number of changes in the same frame
10300
10301   TestIfBadThingTouchesPlayer(x, y);
10302   TestIfPlayerTouchesCustomElement(x, y);
10303   TestIfElementTouchesCustomElement(x, y);
10304 }
10305
10306 static void CreateField(int x, int y, int element)
10307 {
10308   CreateFieldExt(x, y, element, FALSE);
10309 }
10310
10311 static void CreateElementFromChange(int x, int y, int element)
10312 {
10313   element = GET_VALID_RUNTIME_ELEMENT(element);
10314
10315   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10316   {
10317     int old_element = Feld[x][y];
10318
10319     // prevent changed element from moving in same engine frame
10320     // unless both old and new element can either fall or move
10321     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10322         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10323       Stop[x][y] = TRUE;
10324   }
10325
10326   CreateFieldExt(x, y, element, TRUE);
10327 }
10328
10329 static boolean ChangeElement(int x, int y, int element, int page)
10330 {
10331   struct ElementInfo *ei = &element_info[element];
10332   struct ElementChangeInfo *change = &ei->change_page[page];
10333   int ce_value = CustomValue[x][y];
10334   int ce_score = ei->collect_score;
10335   int target_element;
10336   int old_element = Feld[x][y];
10337
10338   // always use default change event to prevent running into a loop
10339   if (ChangeEvent[x][y] == -1)
10340     ChangeEvent[x][y] = CE_DELAY;
10341
10342   if (ChangeEvent[x][y] == CE_DELAY)
10343   {
10344     // reset actual trigger element, trigger player and action element
10345     change->actual_trigger_element = EL_EMPTY;
10346     change->actual_trigger_player = EL_EMPTY;
10347     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10348     change->actual_trigger_side = CH_SIDE_NONE;
10349     change->actual_trigger_ce_value = 0;
10350     change->actual_trigger_ce_score = 0;
10351   }
10352
10353   // do not change elements more than a specified maximum number of changes
10354   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10355     return FALSE;
10356
10357   ChangeCount[x][y]++;          // count number of changes in the same frame
10358
10359   if (change->explode)
10360   {
10361     Bang(x, y);
10362
10363     return TRUE;
10364   }
10365
10366   if (change->use_target_content)
10367   {
10368     boolean complete_replace = TRUE;
10369     boolean can_replace[3][3];
10370     int xx, yy;
10371
10372     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10373     {
10374       boolean is_empty;
10375       boolean is_walkable;
10376       boolean is_diggable;
10377       boolean is_collectible;
10378       boolean is_removable;
10379       boolean is_destructible;
10380       int ex = x + xx - 1;
10381       int ey = y + yy - 1;
10382       int content_element = change->target_content.e[xx][yy];
10383       int e;
10384
10385       can_replace[xx][yy] = TRUE;
10386
10387       if (ex == x && ey == y)   // do not check changing element itself
10388         continue;
10389
10390       if (content_element == EL_EMPTY_SPACE)
10391       {
10392         can_replace[xx][yy] = FALSE;    // do not replace border with space
10393
10394         continue;
10395       }
10396
10397       if (!IN_LEV_FIELD(ex, ey))
10398       {
10399         can_replace[xx][yy] = FALSE;
10400         complete_replace = FALSE;
10401
10402         continue;
10403       }
10404
10405       e = Feld[ex][ey];
10406
10407       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10408         e = MovingOrBlocked2Element(ex, ey);
10409
10410       is_empty = (IS_FREE(ex, ey) ||
10411                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10412
10413       is_walkable     = (is_empty || IS_WALKABLE(e));
10414       is_diggable     = (is_empty || IS_DIGGABLE(e));
10415       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10416       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10417       is_removable    = (is_diggable || is_collectible);
10418
10419       can_replace[xx][yy] =
10420         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10421           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10422           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10423           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10424           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10425           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10426          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10427
10428       if (!can_replace[xx][yy])
10429         complete_replace = FALSE;
10430     }
10431
10432     if (!change->only_if_complete || complete_replace)
10433     {
10434       boolean something_has_changed = FALSE;
10435
10436       if (change->only_if_complete && change->use_random_replace &&
10437           RND(100) < change->random_percentage)
10438         return FALSE;
10439
10440       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10441       {
10442         int ex = x + xx - 1;
10443         int ey = y + yy - 1;
10444         int content_element;
10445
10446         if (can_replace[xx][yy] && (!change->use_random_replace ||
10447                                     RND(100) < change->random_percentage))
10448         {
10449           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10450             RemoveMovingField(ex, ey);
10451
10452           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10453
10454           content_element = change->target_content.e[xx][yy];
10455           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10456                                               ce_value, ce_score);
10457
10458           CreateElementFromChange(ex, ey, target_element);
10459
10460           something_has_changed = TRUE;
10461
10462           // for symmetry reasons, freeze newly created border elements
10463           if (ex != x || ey != y)
10464             Stop[ex][ey] = TRUE;        // no more moving in this frame
10465         }
10466       }
10467
10468       if (something_has_changed)
10469       {
10470         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10471         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10472       }
10473     }
10474   }
10475   else
10476   {
10477     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10478                                         ce_value, ce_score);
10479
10480     if (element == EL_DIAGONAL_GROWING ||
10481         element == EL_DIAGONAL_SHRINKING)
10482     {
10483       target_element = Store[x][y];
10484
10485       Store[x][y] = EL_EMPTY;
10486     }
10487
10488     CreateElementFromChange(x, y, target_element);
10489
10490     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10491     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10492   }
10493
10494   // this uses direct change before indirect change
10495   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10496
10497   return TRUE;
10498 }
10499
10500 static void HandleElementChange(int x, int y, int page)
10501 {
10502   int element = MovingOrBlocked2Element(x, y);
10503   struct ElementInfo *ei = &element_info[element];
10504   struct ElementChangeInfo *change = &ei->change_page[page];
10505   boolean handle_action_before_change = FALSE;
10506
10507 #ifdef DEBUG
10508   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10509       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10510   {
10511     printf("\n\n");
10512     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10513            x, y, element, element_info[element].token_name);
10514     printf("HandleElementChange(): This should never happen!\n");
10515     printf("\n\n");
10516   }
10517 #endif
10518
10519   // this can happen with classic bombs on walkable, changing elements
10520   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10521   {
10522     return;
10523   }
10524
10525   if (ChangeDelay[x][y] == 0)           // initialize element change
10526   {
10527     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10528
10529     if (change->can_change)
10530     {
10531       // !!! not clear why graphic animation should be reset at all here !!!
10532       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10533       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10534
10535       /*
10536         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10537
10538         When using an animation frame delay of 1 (this only happens with
10539         "sp_zonk.moving.left/right" in the classic graphics), the default
10540         (non-moving) animation shows wrong animation frames (while the
10541         moving animation, like "sp_zonk.moving.left/right", is correct,
10542         so this graphical bug never shows up with the classic graphics).
10543         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10544         be drawn instead of the correct frames 0,1,2,3. This is caused by
10545         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10546         an element change: First when the change delay ("ChangeDelay[][]")
10547         counter has reached zero after decrementing, then a second time in
10548         the next frame (after "GfxFrame[][]" was already incremented) when
10549         "ChangeDelay[][]" is reset to the initial delay value again.
10550
10551         This causes frame 0 to be drawn twice, while the last frame won't
10552         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10553
10554         As some animations may already be cleverly designed around this bug
10555         (at least the "Snake Bite" snake tail animation does this), it cannot
10556         simply be fixed here without breaking such existing animations.
10557         Unfortunately, it cannot easily be detected if a graphics set was
10558         designed "before" or "after" the bug was fixed. As a workaround,
10559         a new graphics set option "game.graphics_engine_version" was added
10560         to be able to specify the game's major release version for which the
10561         graphics set was designed, which can then be used to decide if the
10562         bugfix should be used (version 4 and above) or not (version 3 or
10563         below, or if no version was specified at all, as with old sets).
10564
10565         (The wrong/fixed animation frames can be tested with the test level set
10566         "test_gfxframe" and level "000", which contains a specially prepared
10567         custom element at level position (x/y) == (11/9) which uses the zonk
10568         animation mentioned above. Using "game.graphics_engine_version: 4"
10569         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10570         This can also be seen from the debug output for this test element.)
10571       */
10572
10573       // when a custom element is about to change (for example by change delay),
10574       // do not reset graphic animation when the custom element is moving
10575       if (game.graphics_engine_version < 4 &&
10576           !IS_MOVING(x, y))
10577       {
10578         ResetGfxAnimation(x, y);
10579         ResetRandomAnimationValue(x, y);
10580       }
10581
10582       if (change->pre_change_function)
10583         change->pre_change_function(x, y);
10584     }
10585   }
10586
10587   ChangeDelay[x][y]--;
10588
10589   if (ChangeDelay[x][y] != 0)           // continue element change
10590   {
10591     if (change->can_change)
10592     {
10593       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10594
10595       if (IS_ANIMATED(graphic))
10596         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10597
10598       if (change->change_function)
10599         change->change_function(x, y);
10600     }
10601   }
10602   else                                  // finish element change
10603   {
10604     if (ChangePage[x][y] != -1)         // remember page from delayed change
10605     {
10606       page = ChangePage[x][y];
10607       ChangePage[x][y] = -1;
10608
10609       change = &ei->change_page[page];
10610     }
10611
10612     if (IS_MOVING(x, y))                // never change a running system ;-)
10613     {
10614       ChangeDelay[x][y] = 1;            // try change after next move step
10615       ChangePage[x][y] = page;          // remember page to use for change
10616
10617       return;
10618     }
10619
10620     // special case: set new level random seed before changing element
10621     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10622       handle_action_before_change = TRUE;
10623
10624     if (change->has_action && handle_action_before_change)
10625       ExecuteCustomElementAction(x, y, element, page);
10626
10627     if (change->can_change)
10628     {
10629       if (ChangeElement(x, y, element, page))
10630       {
10631         if (change->post_change_function)
10632           change->post_change_function(x, y);
10633       }
10634     }
10635
10636     if (change->has_action && !handle_action_before_change)
10637       ExecuteCustomElementAction(x, y, element, page);
10638   }
10639 }
10640
10641 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10642                                               int trigger_element,
10643                                               int trigger_event,
10644                                               int trigger_player,
10645                                               int trigger_side,
10646                                               int trigger_page)
10647 {
10648   boolean change_done_any = FALSE;
10649   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10650   int i;
10651
10652   if (!(trigger_events[trigger_element][trigger_event]))
10653     return FALSE;
10654
10655   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10656
10657   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10658   {
10659     int element = EL_CUSTOM_START + i;
10660     boolean change_done = FALSE;
10661     int p;
10662
10663     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10664         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10665       continue;
10666
10667     for (p = 0; p < element_info[element].num_change_pages; p++)
10668     {
10669       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10670
10671       if (change->can_change_or_has_action &&
10672           change->has_event[trigger_event] &&
10673           change->trigger_side & trigger_side &&
10674           change->trigger_player & trigger_player &&
10675           change->trigger_page & trigger_page_bits &&
10676           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10677       {
10678         change->actual_trigger_element = trigger_element;
10679         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10680         change->actual_trigger_player_bits = trigger_player;
10681         change->actual_trigger_side = trigger_side;
10682         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10683         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10684
10685         if ((change->can_change && !change_done) || change->has_action)
10686         {
10687           int x, y;
10688
10689           SCAN_PLAYFIELD(x, y)
10690           {
10691             if (Feld[x][y] == element)
10692             {
10693               if (change->can_change && !change_done)
10694               {
10695                 // if element already changed in this frame, not only prevent
10696                 // another element change (checked in ChangeElement()), but
10697                 // also prevent additional element actions for this element
10698
10699                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10700                     !level.use_action_after_change_bug)
10701                   continue;
10702
10703                 ChangeDelay[x][y] = 1;
10704                 ChangeEvent[x][y] = trigger_event;
10705
10706                 HandleElementChange(x, y, p);
10707               }
10708               else if (change->has_action)
10709               {
10710                 // if element already changed in this frame, not only prevent
10711                 // another element change (checked in ChangeElement()), but
10712                 // also prevent additional element actions for this element
10713
10714                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10715                     !level.use_action_after_change_bug)
10716                   continue;
10717
10718                 ExecuteCustomElementAction(x, y, element, p);
10719                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10720               }
10721             }
10722           }
10723
10724           if (change->can_change)
10725           {
10726             change_done = TRUE;
10727             change_done_any = TRUE;
10728           }
10729         }
10730       }
10731     }
10732   }
10733
10734   RECURSION_LOOP_DETECTION_END();
10735
10736   return change_done_any;
10737 }
10738
10739 static boolean CheckElementChangeExt(int x, int y,
10740                                      int element,
10741                                      int trigger_element,
10742                                      int trigger_event,
10743                                      int trigger_player,
10744                                      int trigger_side)
10745 {
10746   boolean change_done = FALSE;
10747   int p;
10748
10749   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10750       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10751     return FALSE;
10752
10753   if (Feld[x][y] == EL_BLOCKED)
10754   {
10755     Blocked2Moving(x, y, &x, &y);
10756     element = Feld[x][y];
10757   }
10758
10759   // check if element has already changed or is about to change after moving
10760   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10761        Feld[x][y] != element) ||
10762
10763       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10764        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10765         ChangePage[x][y] != -1)))
10766     return FALSE;
10767
10768   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10769
10770   for (p = 0; p < element_info[element].num_change_pages; p++)
10771   {
10772     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10773
10774     /* check trigger element for all events where the element that is checked
10775        for changing interacts with a directly adjacent element -- this is
10776        different to element changes that affect other elements to change on the
10777        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10778     boolean check_trigger_element =
10779       (trigger_event == CE_TOUCHING_X ||
10780        trigger_event == CE_HITTING_X ||
10781        trigger_event == CE_HIT_BY_X ||
10782        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10783
10784     if (change->can_change_or_has_action &&
10785         change->has_event[trigger_event] &&
10786         change->trigger_side & trigger_side &&
10787         change->trigger_player & trigger_player &&
10788         (!check_trigger_element ||
10789          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10790     {
10791       change->actual_trigger_element = trigger_element;
10792       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10793       change->actual_trigger_player_bits = trigger_player;
10794       change->actual_trigger_side = trigger_side;
10795       change->actual_trigger_ce_value = CustomValue[x][y];
10796       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10797
10798       // special case: trigger element not at (x,y) position for some events
10799       if (check_trigger_element)
10800       {
10801         static struct
10802         {
10803           int dx, dy;
10804         } move_xy[] =
10805           {
10806             {  0,  0 },
10807             { -1,  0 },
10808             { +1,  0 },
10809             {  0,  0 },
10810             {  0, -1 },
10811             {  0,  0 }, { 0, 0 }, { 0, 0 },
10812             {  0, +1 }
10813           };
10814
10815         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10816         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10817
10818         change->actual_trigger_ce_value = CustomValue[xx][yy];
10819         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10820       }
10821
10822       if (change->can_change && !change_done)
10823       {
10824         ChangeDelay[x][y] = 1;
10825         ChangeEvent[x][y] = trigger_event;
10826
10827         HandleElementChange(x, y, p);
10828
10829         change_done = TRUE;
10830       }
10831       else if (change->has_action)
10832       {
10833         ExecuteCustomElementAction(x, y, element, p);
10834         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10835       }
10836     }
10837   }
10838
10839   RECURSION_LOOP_DETECTION_END();
10840
10841   return change_done;
10842 }
10843
10844 static void PlayPlayerSound(struct PlayerInfo *player)
10845 {
10846   int jx = player->jx, jy = player->jy;
10847   int sound_element = player->artwork_element;
10848   int last_action = player->last_action_waiting;
10849   int action = player->action_waiting;
10850
10851   if (player->is_waiting)
10852   {
10853     if (action != last_action)
10854       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10855     else
10856       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10857   }
10858   else
10859   {
10860     if (action != last_action)
10861       StopSound(element_info[sound_element].sound[last_action]);
10862
10863     if (last_action == ACTION_SLEEPING)
10864       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10865   }
10866 }
10867
10868 static void PlayAllPlayersSound(void)
10869 {
10870   int i;
10871
10872   for (i = 0; i < MAX_PLAYERS; i++)
10873     if (stored_player[i].active)
10874       PlayPlayerSound(&stored_player[i]);
10875 }
10876
10877 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10878 {
10879   boolean last_waiting = player->is_waiting;
10880   int move_dir = player->MovDir;
10881
10882   player->dir_waiting = move_dir;
10883   player->last_action_waiting = player->action_waiting;
10884
10885   if (is_waiting)
10886   {
10887     if (!last_waiting)          // not waiting -> waiting
10888     {
10889       player->is_waiting = TRUE;
10890
10891       player->frame_counter_bored =
10892         FrameCounter +
10893         game.player_boring_delay_fixed +
10894         GetSimpleRandom(game.player_boring_delay_random);
10895       player->frame_counter_sleeping =
10896         FrameCounter +
10897         game.player_sleeping_delay_fixed +
10898         GetSimpleRandom(game.player_sleeping_delay_random);
10899
10900       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10901     }
10902
10903     if (game.player_sleeping_delay_fixed +
10904         game.player_sleeping_delay_random > 0 &&
10905         player->anim_delay_counter == 0 &&
10906         player->post_delay_counter == 0 &&
10907         FrameCounter >= player->frame_counter_sleeping)
10908       player->is_sleeping = TRUE;
10909     else if (game.player_boring_delay_fixed +
10910              game.player_boring_delay_random > 0 &&
10911              FrameCounter >= player->frame_counter_bored)
10912       player->is_bored = TRUE;
10913
10914     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10915                               player->is_bored ? ACTION_BORING :
10916                               ACTION_WAITING);
10917
10918     if (player->is_sleeping && player->use_murphy)
10919     {
10920       // special case for sleeping Murphy when leaning against non-free tile
10921
10922       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10923           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10924            !IS_MOVING(player->jx - 1, player->jy)))
10925         move_dir = MV_LEFT;
10926       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10927                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10928                 !IS_MOVING(player->jx + 1, player->jy)))
10929         move_dir = MV_RIGHT;
10930       else
10931         player->is_sleeping = FALSE;
10932
10933       player->dir_waiting = move_dir;
10934     }
10935
10936     if (player->is_sleeping)
10937     {
10938       if (player->num_special_action_sleeping > 0)
10939       {
10940         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10941         {
10942           int last_special_action = player->special_action_sleeping;
10943           int num_special_action = player->num_special_action_sleeping;
10944           int special_action =
10945             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10946              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10947              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10948              last_special_action + 1 : ACTION_SLEEPING);
10949           int special_graphic =
10950             el_act_dir2img(player->artwork_element, special_action, move_dir);
10951
10952           player->anim_delay_counter =
10953             graphic_info[special_graphic].anim_delay_fixed +
10954             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10955           player->post_delay_counter =
10956             graphic_info[special_graphic].post_delay_fixed +
10957             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10958
10959           player->special_action_sleeping = special_action;
10960         }
10961
10962         if (player->anim_delay_counter > 0)
10963         {
10964           player->action_waiting = player->special_action_sleeping;
10965           player->anim_delay_counter--;
10966         }
10967         else if (player->post_delay_counter > 0)
10968         {
10969           player->post_delay_counter--;
10970         }
10971       }
10972     }
10973     else if (player->is_bored)
10974     {
10975       if (player->num_special_action_bored > 0)
10976       {
10977         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10978         {
10979           int special_action =
10980             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10981           int special_graphic =
10982             el_act_dir2img(player->artwork_element, special_action, move_dir);
10983
10984           player->anim_delay_counter =
10985             graphic_info[special_graphic].anim_delay_fixed +
10986             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10987           player->post_delay_counter =
10988             graphic_info[special_graphic].post_delay_fixed +
10989             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10990
10991           player->special_action_bored = special_action;
10992         }
10993
10994         if (player->anim_delay_counter > 0)
10995         {
10996           player->action_waiting = player->special_action_bored;
10997           player->anim_delay_counter--;
10998         }
10999         else if (player->post_delay_counter > 0)
11000         {
11001           player->post_delay_counter--;
11002         }
11003       }
11004     }
11005   }
11006   else if (last_waiting)        // waiting -> not waiting
11007   {
11008     player->is_waiting = FALSE;
11009     player->is_bored = FALSE;
11010     player->is_sleeping = FALSE;
11011
11012     player->frame_counter_bored = -1;
11013     player->frame_counter_sleeping = -1;
11014
11015     player->anim_delay_counter = 0;
11016     player->post_delay_counter = 0;
11017
11018     player->dir_waiting = player->MovDir;
11019     player->action_waiting = ACTION_DEFAULT;
11020
11021     player->special_action_bored = ACTION_DEFAULT;
11022     player->special_action_sleeping = ACTION_DEFAULT;
11023   }
11024 }
11025
11026 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11027 {
11028   if ((!player->is_moving  && player->was_moving) ||
11029       (player->MovPos == 0 && player->was_moving) ||
11030       (player->is_snapping && !player->was_snapping) ||
11031       (player->is_dropping && !player->was_dropping))
11032   {
11033     if (!CheckSaveEngineSnapshotToList())
11034       return;
11035
11036     player->was_moving = FALSE;
11037     player->was_snapping = TRUE;
11038     player->was_dropping = TRUE;
11039   }
11040   else
11041   {
11042     if (player->is_moving)
11043       player->was_moving = TRUE;
11044
11045     if (!player->is_snapping)
11046       player->was_snapping = FALSE;
11047
11048     if (!player->is_dropping)
11049       player->was_dropping = FALSE;
11050   }
11051 }
11052
11053 static void CheckSingleStepMode(struct PlayerInfo *player)
11054 {
11055   if (tape.single_step && tape.recording && !tape.pausing)
11056   {
11057     /* as it is called "single step mode", just return to pause mode when the
11058        player stopped moving after one tile (or never starts moving at all) */
11059     if (!player->is_moving &&
11060         !player->is_pushing &&
11061         !player->is_dropping_pressed)
11062       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11063   }
11064
11065   CheckSaveEngineSnapshot(player);
11066 }
11067
11068 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11069 {
11070   int left      = player_action & JOY_LEFT;
11071   int right     = player_action & JOY_RIGHT;
11072   int up        = player_action & JOY_UP;
11073   int down      = player_action & JOY_DOWN;
11074   int button1   = player_action & JOY_BUTTON_1;
11075   int button2   = player_action & JOY_BUTTON_2;
11076   int dx        = (left ? -1 : right ? 1 : 0);
11077   int dy        = (up   ? -1 : down  ? 1 : 0);
11078
11079   if (!player->active || tape.pausing)
11080     return 0;
11081
11082   if (player_action)
11083   {
11084     if (button1)
11085       SnapField(player, dx, dy);
11086     else
11087     {
11088       if (button2)
11089         DropElement(player);
11090
11091       MovePlayer(player, dx, dy);
11092     }
11093
11094     CheckSingleStepMode(player);
11095
11096     SetPlayerWaiting(player, FALSE);
11097
11098     return player_action;
11099   }
11100   else
11101   {
11102     // no actions for this player (no input at player's configured device)
11103
11104     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11105     SnapField(player, 0, 0);
11106     CheckGravityMovementWhenNotMoving(player);
11107
11108     if (player->MovPos == 0)
11109       SetPlayerWaiting(player, TRUE);
11110
11111     if (player->MovPos == 0)    // needed for tape.playing
11112       player->is_moving = FALSE;
11113
11114     player->is_dropping = FALSE;
11115     player->is_dropping_pressed = FALSE;
11116     player->drop_pressed_delay = 0;
11117
11118     CheckSingleStepMode(player);
11119
11120     return 0;
11121   }
11122 }
11123
11124 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11125                                          byte *tape_action)
11126 {
11127   if (!tape.use_mouse)
11128     return;
11129
11130   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11131   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11132   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11133 }
11134
11135 static void SetTapeActionFromMouseAction(byte *tape_action,
11136                                          struct MouseActionInfo *mouse_action)
11137 {
11138   if (!tape.use_mouse)
11139     return;
11140
11141   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11142   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11143   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11144 }
11145
11146 static void CheckLevelSolved(void)
11147 {
11148   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11149   {
11150     if (game_em.level_solved &&
11151         !game_em.game_over)                             // game won
11152     {
11153       LevelSolved();
11154
11155       game_em.game_over = TRUE;
11156
11157       game.all_players_gone = TRUE;
11158     }
11159
11160     if (game_em.game_over)                              // game lost
11161       game.all_players_gone = TRUE;
11162   }
11163   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11164   {
11165     if (game_sp.level_solved &&
11166         !game_sp.game_over)                             // game won
11167     {
11168       LevelSolved();
11169
11170       game_sp.game_over = TRUE;
11171
11172       game.all_players_gone = TRUE;
11173     }
11174
11175     if (game_sp.game_over)                              // game lost
11176       game.all_players_gone = TRUE;
11177   }
11178   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11179   {
11180     if (game_mm.level_solved &&
11181         !game_mm.game_over)                             // game won
11182     {
11183       LevelSolved();
11184
11185       game_mm.game_over = TRUE;
11186
11187       game.all_players_gone = TRUE;
11188     }
11189
11190     if (game_mm.game_over)                              // game lost
11191       game.all_players_gone = TRUE;
11192   }
11193 }
11194
11195 static void CheckLevelTime(void)
11196 {
11197   int i;
11198
11199   if (TimeFrames >= FRAMES_PER_SECOND)
11200   {
11201     TimeFrames = 0;
11202     TapeTime++;
11203
11204     for (i = 0; i < MAX_PLAYERS; i++)
11205     {
11206       struct PlayerInfo *player = &stored_player[i];
11207
11208       if (SHIELD_ON(player))
11209       {
11210         player->shield_normal_time_left--;
11211
11212         if (player->shield_deadly_time_left > 0)
11213           player->shield_deadly_time_left--;
11214       }
11215     }
11216
11217     if (!game.LevelSolved && !level.use_step_counter)
11218     {
11219       TimePlayed++;
11220
11221       if (TimeLeft > 0)
11222       {
11223         TimeLeft--;
11224
11225         if (TimeLeft <= 10 && setup.time_limit)
11226           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11227
11228         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11229            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11230
11231         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11232
11233         if (!TimeLeft && setup.time_limit)
11234         {
11235           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11236             level.native_em_level->lev->killed_out_of_time = TRUE;
11237           else
11238             for (i = 0; i < MAX_PLAYERS; i++)
11239               KillPlayer(&stored_player[i]);
11240         }
11241       }
11242       else if (game.no_time_limit && !game.all_players_gone)
11243       {
11244         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11245       }
11246
11247       level.native_em_level->lev->time =
11248         (game.no_time_limit ? TimePlayed : TimeLeft);
11249     }
11250
11251     if (tape.recording || tape.playing)
11252       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11253   }
11254
11255   if (tape.recording || tape.playing)
11256     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11257
11258   UpdateAndDisplayGameControlValues();
11259 }
11260
11261 void AdvanceFrameAndPlayerCounters(int player_nr)
11262 {
11263   int i;
11264
11265   // advance frame counters (global frame counter and time frame counter)
11266   FrameCounter++;
11267   TimeFrames++;
11268
11269   // advance player counters (counters for move delay, move animation etc.)
11270   for (i = 0; i < MAX_PLAYERS; i++)
11271   {
11272     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11273     int move_delay_value = stored_player[i].move_delay_value;
11274     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11275
11276     if (!advance_player_counters)       // not all players may be affected
11277       continue;
11278
11279     if (move_frames == 0)       // less than one move per game frame
11280     {
11281       int stepsize = TILEX / move_delay_value;
11282       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11283       int count = (stored_player[i].is_moving ?
11284                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11285
11286       if (count % delay == 0)
11287         move_frames = 1;
11288     }
11289
11290     stored_player[i].Frame += move_frames;
11291
11292     if (stored_player[i].MovPos != 0)
11293       stored_player[i].StepFrame += move_frames;
11294
11295     if (stored_player[i].move_delay > 0)
11296       stored_player[i].move_delay--;
11297
11298     // due to bugs in previous versions, counter must count up, not down
11299     if (stored_player[i].push_delay != -1)
11300       stored_player[i].push_delay++;
11301
11302     if (stored_player[i].drop_delay > 0)
11303       stored_player[i].drop_delay--;
11304
11305     if (stored_player[i].is_dropping_pressed)
11306       stored_player[i].drop_pressed_delay++;
11307   }
11308 }
11309
11310 void StartGameActions(boolean init_network_game, boolean record_tape,
11311                       int random_seed)
11312 {
11313   unsigned int new_random_seed = InitRND(random_seed);
11314
11315   if (record_tape)
11316     TapeStartRecording(new_random_seed);
11317
11318   if (init_network_game)
11319   {
11320     SendToServer_LevelFile();
11321     SendToServer_StartPlaying();
11322
11323     return;
11324   }
11325
11326   InitGame();
11327 }
11328
11329 static void GameActionsExt(void)
11330 {
11331 #if 0
11332   static unsigned int game_frame_delay = 0;
11333 #endif
11334   unsigned int game_frame_delay_value;
11335   byte *recorded_player_action;
11336   byte summarized_player_action = 0;
11337   byte tape_action[MAX_PLAYERS];
11338   int i;
11339
11340   // detect endless loops, caused by custom element programming
11341   if (recursion_loop_detected && recursion_loop_depth == 0)
11342   {
11343     char *message = getStringCat3("Internal Error! Element ",
11344                                   EL_NAME(recursion_loop_element),
11345                                   " caused endless loop! Quit the game?");
11346
11347     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11348           EL_NAME(recursion_loop_element));
11349
11350     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11351
11352     recursion_loop_detected = FALSE;    // if game should be continued
11353
11354     free(message);
11355
11356     return;
11357   }
11358
11359   if (game.restart_level)
11360     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11361
11362   CheckLevelSolved();
11363
11364   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11365     GameWon();
11366
11367   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11368     TapeStop();
11369
11370   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11371     return;
11372
11373   game_frame_delay_value =
11374     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11375
11376   if (tape.playing && tape.warp_forward && !tape.pausing)
11377     game_frame_delay_value = 0;
11378
11379   SetVideoFrameDelay(game_frame_delay_value);
11380
11381   // (de)activate virtual buttons depending on current game status
11382   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11383   {
11384     if (game.all_players_gone)  // if no players there to be controlled anymore
11385       SetOverlayActive(FALSE);
11386     else if (!tape.playing)     // if game continues after tape stopped playing
11387       SetOverlayActive(TRUE);
11388   }
11389
11390 #if 0
11391 #if 0
11392   // ---------- main game synchronization point ----------
11393
11394   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11395
11396   printf("::: skip == %d\n", skip);
11397
11398 #else
11399   // ---------- main game synchronization point ----------
11400
11401   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11402 #endif
11403 #endif
11404
11405   if (network_playing && !network_player_action_received)
11406   {
11407     // try to get network player actions in time
11408
11409     // last chance to get network player actions without main loop delay
11410     HandleNetworking();
11411
11412     // game was quit by network peer
11413     if (game_status != GAME_MODE_PLAYING)
11414       return;
11415
11416     // check if network player actions still missing and game still running
11417     if (!network_player_action_received && !checkGameEnded())
11418       return;           // failed to get network player actions in time
11419
11420     // do not yet reset "network_player_action_received" (for tape.pausing)
11421   }
11422
11423   if (tape.pausing)
11424     return;
11425
11426   // at this point we know that we really continue executing the game
11427
11428   network_player_action_received = FALSE;
11429
11430   // when playing tape, read previously recorded player input from tape data
11431   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11432
11433   local_player->effective_mouse_action = local_player->mouse_action;
11434
11435   if (recorded_player_action != NULL)
11436     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11437                                  recorded_player_action);
11438
11439   // TapePlayAction() may return NULL when toggling to "pause before death"
11440   if (tape.pausing)
11441     return;
11442
11443   if (tape.set_centered_player)
11444   {
11445     game.centered_player_nr_next = tape.centered_player_nr_next;
11446     game.set_centered_player = TRUE;
11447   }
11448
11449   for (i = 0; i < MAX_PLAYERS; i++)
11450   {
11451     summarized_player_action |= stored_player[i].action;
11452
11453     if (!network_playing && (game.team_mode || tape.playing))
11454       stored_player[i].effective_action = stored_player[i].action;
11455   }
11456
11457   if (network_playing && !checkGameEnded())
11458     SendToServer_MovePlayer(summarized_player_action);
11459
11460   // summarize all actions at local players mapped input device position
11461   // (this allows using different input devices in single player mode)
11462   if (!network.enabled && !game.team_mode)
11463     stored_player[map_player_action[local_player->index_nr]].effective_action =
11464       summarized_player_action;
11465
11466   if (tape.recording &&
11467       setup.team_mode &&
11468       setup.input_on_focus &&
11469       game.centered_player_nr != -1)
11470   {
11471     for (i = 0; i < MAX_PLAYERS; i++)
11472       stored_player[i].effective_action =
11473         (i == game.centered_player_nr ? summarized_player_action : 0);
11474   }
11475
11476   if (recorded_player_action != NULL)
11477     for (i = 0; i < MAX_PLAYERS; i++)
11478       stored_player[i].effective_action = recorded_player_action[i];
11479
11480   for (i = 0; i < MAX_PLAYERS; i++)
11481   {
11482     tape_action[i] = stored_player[i].effective_action;
11483
11484     /* (this may happen in the RND game engine if a player was not present on
11485        the playfield on level start, but appeared later from a custom element */
11486     if (setup.team_mode &&
11487         tape.recording &&
11488         tape_action[i] &&
11489         !tape.player_participates[i])
11490       tape.player_participates[i] = TRUE;
11491   }
11492
11493   SetTapeActionFromMouseAction(tape_action,
11494                                &local_player->effective_mouse_action);
11495
11496   // only record actions from input devices, but not programmed actions
11497   if (tape.recording)
11498     TapeRecordAction(tape_action);
11499
11500   // remember if game was played (especially after tape stopped playing)
11501   if (!tape.playing && summarized_player_action)
11502     game.GamePlayed = TRUE;
11503
11504 #if USE_NEW_PLAYER_ASSIGNMENTS
11505   // !!! also map player actions in single player mode !!!
11506   // if (game.team_mode)
11507   if (1)
11508   {
11509     byte mapped_action[MAX_PLAYERS];
11510
11511 #if DEBUG_PLAYER_ACTIONS
11512     printf(":::");
11513     for (i = 0; i < MAX_PLAYERS; i++)
11514       printf(" %d, ", stored_player[i].effective_action);
11515 #endif
11516
11517     for (i = 0; i < MAX_PLAYERS; i++)
11518       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11519
11520     for (i = 0; i < MAX_PLAYERS; i++)
11521       stored_player[i].effective_action = mapped_action[i];
11522
11523 #if DEBUG_PLAYER_ACTIONS
11524     printf(" =>");
11525     for (i = 0; i < MAX_PLAYERS; i++)
11526       printf(" %d, ", stored_player[i].effective_action);
11527     printf("\n");
11528 #endif
11529   }
11530 #if DEBUG_PLAYER_ACTIONS
11531   else
11532   {
11533     printf(":::");
11534     for (i = 0; i < MAX_PLAYERS; i++)
11535       printf(" %d, ", stored_player[i].effective_action);
11536     printf("\n");
11537   }
11538 #endif
11539 #endif
11540
11541   for (i = 0; i < MAX_PLAYERS; i++)
11542   {
11543     // allow engine snapshot in case of changed movement attempt
11544     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11545         (stored_player[i].effective_action & KEY_MOTION))
11546       game.snapshot.changed_action = TRUE;
11547
11548     // allow engine snapshot in case of snapping/dropping attempt
11549     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11550         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11551       game.snapshot.changed_action = TRUE;
11552
11553     game.snapshot.last_action[i] = stored_player[i].effective_action;
11554   }
11555
11556   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11557   {
11558     GameActions_EM_Main();
11559   }
11560   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11561   {
11562     GameActions_SP_Main();
11563   }
11564   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11565   {
11566     GameActions_MM_Main();
11567   }
11568   else
11569   {
11570     GameActions_RND_Main();
11571   }
11572
11573   BlitScreenToBitmap(backbuffer);
11574
11575   CheckLevelSolved();
11576   CheckLevelTime();
11577
11578   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11579
11580   if (global.show_frames_per_second)
11581   {
11582     static unsigned int fps_counter = 0;
11583     static int fps_frames = 0;
11584     unsigned int fps_delay_ms = Counter() - fps_counter;
11585
11586     fps_frames++;
11587
11588     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11589     {
11590       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11591
11592       fps_frames = 0;
11593       fps_counter = Counter();
11594
11595       // always draw FPS to screen after FPS value was updated
11596       redraw_mask |= REDRAW_FPS;
11597     }
11598
11599     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11600     if (GetDrawDeactivationMask() == REDRAW_NONE)
11601       redraw_mask |= REDRAW_FPS;
11602   }
11603 }
11604
11605 static void GameActions_CheckSaveEngineSnapshot(void)
11606 {
11607   if (!game.snapshot.save_snapshot)
11608     return;
11609
11610   // clear flag for saving snapshot _before_ saving snapshot
11611   game.snapshot.save_snapshot = FALSE;
11612
11613   SaveEngineSnapshotToList();
11614 }
11615
11616 void GameActions(void)
11617 {
11618   GameActionsExt();
11619
11620   GameActions_CheckSaveEngineSnapshot();
11621 }
11622
11623 void GameActions_EM_Main(void)
11624 {
11625   byte effective_action[MAX_PLAYERS];
11626   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11627   int i;
11628
11629   for (i = 0; i < MAX_PLAYERS; i++)
11630     effective_action[i] = stored_player[i].effective_action;
11631
11632   GameActions_EM(effective_action, warp_mode);
11633 }
11634
11635 void GameActions_SP_Main(void)
11636 {
11637   byte effective_action[MAX_PLAYERS];
11638   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11639   int i;
11640
11641   for (i = 0; i < MAX_PLAYERS; i++)
11642     effective_action[i] = stored_player[i].effective_action;
11643
11644   GameActions_SP(effective_action, warp_mode);
11645
11646   for (i = 0; i < MAX_PLAYERS; i++)
11647   {
11648     if (stored_player[i].force_dropping)
11649       stored_player[i].action |= KEY_BUTTON_DROP;
11650
11651     stored_player[i].force_dropping = FALSE;
11652   }
11653 }
11654
11655 void GameActions_MM_Main(void)
11656 {
11657   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11658
11659   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11660 }
11661
11662 void GameActions_RND_Main(void)
11663 {
11664   GameActions_RND();
11665 }
11666
11667 void GameActions_RND(void)
11668 {
11669   int magic_wall_x = 0, magic_wall_y = 0;
11670   int i, x, y, element, graphic, last_gfx_frame;
11671
11672   InitPlayfieldScanModeVars();
11673
11674   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11675   {
11676     SCAN_PLAYFIELD(x, y)
11677     {
11678       ChangeCount[x][y] = 0;
11679       ChangeEvent[x][y] = -1;
11680     }
11681   }
11682
11683   if (game.set_centered_player)
11684   {
11685     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11686
11687     // switching to "all players" only possible if all players fit to screen
11688     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11689     {
11690       game.centered_player_nr_next = game.centered_player_nr;
11691       game.set_centered_player = FALSE;
11692     }
11693
11694     // do not switch focus to non-existing (or non-active) player
11695     if (game.centered_player_nr_next >= 0 &&
11696         !stored_player[game.centered_player_nr_next].active)
11697     {
11698       game.centered_player_nr_next = game.centered_player_nr;
11699       game.set_centered_player = FALSE;
11700     }
11701   }
11702
11703   if (game.set_centered_player &&
11704       ScreenMovPos == 0)        // screen currently aligned at tile position
11705   {
11706     int sx, sy;
11707
11708     if (game.centered_player_nr_next == -1)
11709     {
11710       setScreenCenteredToAllPlayers(&sx, &sy);
11711     }
11712     else
11713     {
11714       sx = stored_player[game.centered_player_nr_next].jx;
11715       sy = stored_player[game.centered_player_nr_next].jy;
11716     }
11717
11718     game.centered_player_nr = game.centered_player_nr_next;
11719     game.set_centered_player = FALSE;
11720
11721     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11722     DrawGameDoorValues();
11723   }
11724
11725   for (i = 0; i < MAX_PLAYERS; i++)
11726   {
11727     int actual_player_action = stored_player[i].effective_action;
11728
11729 #if 1
11730     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11731        - rnd_equinox_tetrachloride 048
11732        - rnd_equinox_tetrachloride_ii 096
11733        - rnd_emanuel_schmieg 002
11734        - doctor_sloan_ww 001, 020
11735     */
11736     if (stored_player[i].MovPos == 0)
11737       CheckGravityMovement(&stored_player[i]);
11738 #endif
11739
11740     // overwrite programmed action with tape action
11741     if (stored_player[i].programmed_action)
11742       actual_player_action = stored_player[i].programmed_action;
11743
11744     PlayerActions(&stored_player[i], actual_player_action);
11745
11746     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11747   }
11748
11749   ScrollScreen(NULL, SCROLL_GO_ON);
11750
11751   /* for backwards compatibility, the following code emulates a fixed bug that
11752      occured when pushing elements (causing elements that just made their last
11753      pushing step to already (if possible) make their first falling step in the
11754      same game frame, which is bad); this code is also needed to use the famous
11755      "spring push bug" which is used in older levels and might be wanted to be
11756      used also in newer levels, but in this case the buggy pushing code is only
11757      affecting the "spring" element and no other elements */
11758
11759   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11760   {
11761     for (i = 0; i < MAX_PLAYERS; i++)
11762     {
11763       struct PlayerInfo *player = &stored_player[i];
11764       int x = player->jx;
11765       int y = player->jy;
11766
11767       if (player->active && player->is_pushing && player->is_moving &&
11768           IS_MOVING(x, y) &&
11769           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11770            Feld[x][y] == EL_SPRING))
11771       {
11772         ContinueMoving(x, y);
11773
11774         // continue moving after pushing (this is actually a bug)
11775         if (!IS_MOVING(x, y))
11776           Stop[x][y] = FALSE;
11777       }
11778     }
11779   }
11780
11781   SCAN_PLAYFIELD(x, y)
11782   {
11783     Last[x][y] = Feld[x][y];
11784
11785     ChangeCount[x][y] = 0;
11786     ChangeEvent[x][y] = -1;
11787
11788     // this must be handled before main playfield loop
11789     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11790     {
11791       MovDelay[x][y]--;
11792       if (MovDelay[x][y] <= 0)
11793         RemoveField(x, y);
11794     }
11795
11796     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11797     {
11798       MovDelay[x][y]--;
11799       if (MovDelay[x][y] <= 0)
11800       {
11801         RemoveField(x, y);
11802         TEST_DrawLevelField(x, y);
11803
11804         TestIfElementTouchesCustomElement(x, y);        // for empty space
11805       }
11806     }
11807
11808 #if DEBUG
11809     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11810     {
11811       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11812       printf("GameActions(): This should never happen!\n");
11813
11814       ChangePage[x][y] = -1;
11815     }
11816 #endif
11817
11818     Stop[x][y] = FALSE;
11819     if (WasJustMoving[x][y] > 0)
11820       WasJustMoving[x][y]--;
11821     if (WasJustFalling[x][y] > 0)
11822       WasJustFalling[x][y]--;
11823     if (CheckCollision[x][y] > 0)
11824       CheckCollision[x][y]--;
11825     if (CheckImpact[x][y] > 0)
11826       CheckImpact[x][y]--;
11827
11828     GfxFrame[x][y]++;
11829
11830     /* reset finished pushing action (not done in ContinueMoving() to allow
11831        continuous pushing animation for elements with zero push delay) */
11832     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11833     {
11834       ResetGfxAnimation(x, y);
11835       TEST_DrawLevelField(x, y);
11836     }
11837
11838 #if DEBUG
11839     if (IS_BLOCKED(x, y))
11840     {
11841       int oldx, oldy;
11842
11843       Blocked2Moving(x, y, &oldx, &oldy);
11844       if (!IS_MOVING(oldx, oldy))
11845       {
11846         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11847         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11848         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11849         printf("GameActions(): This should never happen!\n");
11850       }
11851     }
11852 #endif
11853   }
11854
11855   SCAN_PLAYFIELD(x, y)
11856   {
11857     element = Feld[x][y];
11858     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11859     last_gfx_frame = GfxFrame[x][y];
11860
11861     ResetGfxFrame(x, y);
11862
11863     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11864       DrawLevelGraphicAnimation(x, y, graphic);
11865
11866     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11867         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11868       ResetRandomAnimationValue(x, y);
11869
11870     SetRandomAnimationValue(x, y);
11871
11872     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11873
11874     if (IS_INACTIVE(element))
11875     {
11876       if (IS_ANIMATED(graphic))
11877         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11878
11879       continue;
11880     }
11881
11882     // this may take place after moving, so 'element' may have changed
11883     if (IS_CHANGING(x, y) &&
11884         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11885     {
11886       int page = element_info[element].event_page_nr[CE_DELAY];
11887
11888       HandleElementChange(x, y, page);
11889
11890       element = Feld[x][y];
11891       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11892     }
11893
11894     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11895     {
11896       StartMoving(x, y);
11897
11898       element = Feld[x][y];
11899       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11900
11901       if (IS_ANIMATED(graphic) &&
11902           !IS_MOVING(x, y) &&
11903           !Stop[x][y])
11904         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11905
11906       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11907         TEST_DrawTwinkleOnField(x, y);
11908     }
11909     else if (element == EL_ACID)
11910     {
11911       if (!Stop[x][y])
11912         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11913     }
11914     else if ((element == EL_EXIT_OPEN ||
11915               element == EL_EM_EXIT_OPEN ||
11916               element == EL_SP_EXIT_OPEN ||
11917               element == EL_STEEL_EXIT_OPEN ||
11918               element == EL_EM_STEEL_EXIT_OPEN ||
11919               element == EL_SP_TERMINAL ||
11920               element == EL_SP_TERMINAL_ACTIVE ||
11921               element == EL_EXTRA_TIME ||
11922               element == EL_SHIELD_NORMAL ||
11923               element == EL_SHIELD_DEADLY) &&
11924              IS_ANIMATED(graphic))
11925       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11926     else if (IS_MOVING(x, y))
11927       ContinueMoving(x, y);
11928     else if (IS_ACTIVE_BOMB(element))
11929       CheckDynamite(x, y);
11930     else if (element == EL_AMOEBA_GROWING)
11931       AmoebeWaechst(x, y);
11932     else if (element == EL_AMOEBA_SHRINKING)
11933       AmoebaDisappearing(x, y);
11934
11935 #if !USE_NEW_AMOEBA_CODE
11936     else if (IS_AMOEBALIVE(element))
11937       AmoebeAbleger(x, y);
11938 #endif
11939
11940     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11941       Life(x, y);
11942     else if (element == EL_EXIT_CLOSED)
11943       CheckExit(x, y);
11944     else if (element == EL_EM_EXIT_CLOSED)
11945       CheckExitEM(x, y);
11946     else if (element == EL_STEEL_EXIT_CLOSED)
11947       CheckExitSteel(x, y);
11948     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11949       CheckExitSteelEM(x, y);
11950     else if (element == EL_SP_EXIT_CLOSED)
11951       CheckExitSP(x, y);
11952     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11953              element == EL_EXPANDABLE_STEELWALL_GROWING)
11954       MauerWaechst(x, y);
11955     else if (element == EL_EXPANDABLE_WALL ||
11956              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11957              element == EL_EXPANDABLE_WALL_VERTICAL ||
11958              element == EL_EXPANDABLE_WALL_ANY ||
11959              element == EL_BD_EXPANDABLE_WALL)
11960       MauerAbleger(x, y);
11961     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11962              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11963              element == EL_EXPANDABLE_STEELWALL_ANY)
11964       MauerAblegerStahl(x, y);
11965     else if (element == EL_FLAMES)
11966       CheckForDragon(x, y);
11967     else if (element == EL_EXPLOSION)
11968       ; // drawing of correct explosion animation is handled separately
11969     else if (element == EL_ELEMENT_SNAPPING ||
11970              element == EL_DIAGONAL_SHRINKING ||
11971              element == EL_DIAGONAL_GROWING)
11972     {
11973       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11974
11975       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11976     }
11977     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11978       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11979
11980     if (IS_BELT_ACTIVE(element))
11981       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11982
11983     if (game.magic_wall_active)
11984     {
11985       int jx = local_player->jx, jy = local_player->jy;
11986
11987       // play the element sound at the position nearest to the player
11988       if ((element == EL_MAGIC_WALL_FULL ||
11989            element == EL_MAGIC_WALL_ACTIVE ||
11990            element == EL_MAGIC_WALL_EMPTYING ||
11991            element == EL_BD_MAGIC_WALL_FULL ||
11992            element == EL_BD_MAGIC_WALL_ACTIVE ||
11993            element == EL_BD_MAGIC_WALL_EMPTYING ||
11994            element == EL_DC_MAGIC_WALL_FULL ||
11995            element == EL_DC_MAGIC_WALL_ACTIVE ||
11996            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11997           ABS(x - jx) + ABS(y - jy) <
11998           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
11999       {
12000         magic_wall_x = x;
12001         magic_wall_y = y;
12002       }
12003     }
12004   }
12005
12006 #if USE_NEW_AMOEBA_CODE
12007   // new experimental amoeba growth stuff
12008   if (!(FrameCounter % 8))
12009   {
12010     static unsigned int random = 1684108901;
12011
12012     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12013     {
12014       x = RND(lev_fieldx);
12015       y = RND(lev_fieldy);
12016       element = Feld[x][y];
12017
12018       if (!IS_PLAYER(x,y) &&
12019           (element == EL_EMPTY ||
12020            CAN_GROW_INTO(element) ||
12021            element == EL_QUICKSAND_EMPTY ||
12022            element == EL_QUICKSAND_FAST_EMPTY ||
12023            element == EL_ACID_SPLASH_LEFT ||
12024            element == EL_ACID_SPLASH_RIGHT))
12025       {
12026         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12027             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12028             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12029             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12030           Feld[x][y] = EL_AMOEBA_DROP;
12031       }
12032
12033       random = random * 129 + 1;
12034     }
12035   }
12036 #endif
12037
12038   game.explosions_delayed = FALSE;
12039
12040   SCAN_PLAYFIELD(x, y)
12041   {
12042     element = Feld[x][y];
12043
12044     if (ExplodeField[x][y])
12045       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12046     else if (element == EL_EXPLOSION)
12047       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12048
12049     ExplodeField[x][y] = EX_TYPE_NONE;
12050   }
12051
12052   game.explosions_delayed = TRUE;
12053
12054   if (game.magic_wall_active)
12055   {
12056     if (!(game.magic_wall_time_left % 4))
12057     {
12058       int element = Feld[magic_wall_x][magic_wall_y];
12059
12060       if (element == EL_BD_MAGIC_WALL_FULL ||
12061           element == EL_BD_MAGIC_WALL_ACTIVE ||
12062           element == EL_BD_MAGIC_WALL_EMPTYING)
12063         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12064       else if (element == EL_DC_MAGIC_WALL_FULL ||
12065                element == EL_DC_MAGIC_WALL_ACTIVE ||
12066                element == EL_DC_MAGIC_WALL_EMPTYING)
12067         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12068       else
12069         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12070     }
12071
12072     if (game.magic_wall_time_left > 0)
12073     {
12074       game.magic_wall_time_left--;
12075
12076       if (!game.magic_wall_time_left)
12077       {
12078         SCAN_PLAYFIELD(x, y)
12079         {
12080           element = Feld[x][y];
12081
12082           if (element == EL_MAGIC_WALL_ACTIVE ||
12083               element == EL_MAGIC_WALL_FULL)
12084           {
12085             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12086             TEST_DrawLevelField(x, y);
12087           }
12088           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12089                    element == EL_BD_MAGIC_WALL_FULL)
12090           {
12091             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12092             TEST_DrawLevelField(x, y);
12093           }
12094           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12095                    element == EL_DC_MAGIC_WALL_FULL)
12096           {
12097             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12098             TEST_DrawLevelField(x, y);
12099           }
12100         }
12101
12102         game.magic_wall_active = FALSE;
12103       }
12104     }
12105   }
12106
12107   if (game.light_time_left > 0)
12108   {
12109     game.light_time_left--;
12110
12111     if (game.light_time_left == 0)
12112       RedrawAllLightSwitchesAndInvisibleElements();
12113   }
12114
12115   if (game.timegate_time_left > 0)
12116   {
12117     game.timegate_time_left--;
12118
12119     if (game.timegate_time_left == 0)
12120       CloseAllOpenTimegates();
12121   }
12122
12123   if (game.lenses_time_left > 0)
12124   {
12125     game.lenses_time_left--;
12126
12127     if (game.lenses_time_left == 0)
12128       RedrawAllInvisibleElementsForLenses();
12129   }
12130
12131   if (game.magnify_time_left > 0)
12132   {
12133     game.magnify_time_left--;
12134
12135     if (game.magnify_time_left == 0)
12136       RedrawAllInvisibleElementsForMagnifier();
12137   }
12138
12139   for (i = 0; i < MAX_PLAYERS; i++)
12140   {
12141     struct PlayerInfo *player = &stored_player[i];
12142
12143     if (SHIELD_ON(player))
12144     {
12145       if (player->shield_deadly_time_left)
12146         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12147       else if (player->shield_normal_time_left)
12148         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12149     }
12150   }
12151
12152 #if USE_DELAYED_GFX_REDRAW
12153   SCAN_PLAYFIELD(x, y)
12154   {
12155     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12156     {
12157       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12158          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12159
12160       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12161         DrawLevelField(x, y);
12162
12163       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12164         DrawLevelFieldCrumbled(x, y);
12165
12166       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12167         DrawLevelFieldCrumbledNeighbours(x, y);
12168
12169       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12170         DrawTwinkleOnField(x, y);
12171     }
12172
12173     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12174   }
12175 #endif
12176
12177   DrawAllPlayers();
12178   PlayAllPlayersSound();
12179
12180   if (local_player->show_envelope != 0 && (!local_player->active ||
12181                                            local_player->MovPos == 0))
12182   {
12183     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12184
12185     local_player->show_envelope = 0;
12186   }
12187
12188   // use random number generator in every frame to make it less predictable
12189   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12190     RND(1);
12191 }
12192
12193 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12194 {
12195   int min_x = x, min_y = y, max_x = x, max_y = y;
12196   int i;
12197
12198   for (i = 0; i < MAX_PLAYERS; i++)
12199   {
12200     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12201
12202     if (!stored_player[i].active || &stored_player[i] == player)
12203       continue;
12204
12205     min_x = MIN(min_x, jx);
12206     min_y = MIN(min_y, jy);
12207     max_x = MAX(max_x, jx);
12208     max_y = MAX(max_y, jy);
12209   }
12210
12211   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12212 }
12213
12214 static boolean AllPlayersInVisibleScreen(void)
12215 {
12216   int i;
12217
12218   for (i = 0; i < MAX_PLAYERS; i++)
12219   {
12220     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12221
12222     if (!stored_player[i].active)
12223       continue;
12224
12225     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12226       return FALSE;
12227   }
12228
12229   return TRUE;
12230 }
12231
12232 void ScrollLevel(int dx, int dy)
12233 {
12234   int scroll_offset = 2 * TILEX_VAR;
12235   int x, y;
12236
12237   BlitBitmap(drawto_field, drawto_field,
12238              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12239              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12240              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12241              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12242              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12243              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12244
12245   if (dx != 0)
12246   {
12247     x = (dx == 1 ? BX1 : BX2);
12248     for (y = BY1; y <= BY2; y++)
12249       DrawScreenField(x, y);
12250   }
12251
12252   if (dy != 0)
12253   {
12254     y = (dy == 1 ? BY1 : BY2);
12255     for (x = BX1; x <= BX2; x++)
12256       DrawScreenField(x, y);
12257   }
12258
12259   redraw_mask |= REDRAW_FIELD;
12260 }
12261
12262 static boolean canFallDown(struct PlayerInfo *player)
12263 {
12264   int jx = player->jx, jy = player->jy;
12265
12266   return (IN_LEV_FIELD(jx, jy + 1) &&
12267           (IS_FREE(jx, jy + 1) ||
12268            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12269           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12270           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12271 }
12272
12273 static boolean canPassField(int x, int y, int move_dir)
12274 {
12275   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12276   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12277   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12278   int nextx = x + dx;
12279   int nexty = y + dy;
12280   int element = Feld[x][y];
12281
12282   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12283           !CAN_MOVE(element) &&
12284           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12285           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12286           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12287 }
12288
12289 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12290 {
12291   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12292   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12293   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12294   int newx = x + dx;
12295   int newy = y + dy;
12296
12297   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12298           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12299           (IS_DIGGABLE(Feld[newx][newy]) ||
12300            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12301            canPassField(newx, newy, move_dir)));
12302 }
12303
12304 static void CheckGravityMovement(struct PlayerInfo *player)
12305 {
12306   if (player->gravity && !player->programmed_action)
12307   {
12308     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12309     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12310     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12311     int jx = player->jx, jy = player->jy;
12312     boolean player_is_moving_to_valid_field =
12313       (!player_is_snapping &&
12314        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12315         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12316     boolean player_can_fall_down = canFallDown(player);
12317
12318     if (player_can_fall_down &&
12319         !player_is_moving_to_valid_field)
12320       player->programmed_action = MV_DOWN;
12321   }
12322 }
12323
12324 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12325 {
12326   return CheckGravityMovement(player);
12327
12328   if (player->gravity && !player->programmed_action)
12329   {
12330     int jx = player->jx, jy = player->jy;
12331     boolean field_under_player_is_free =
12332       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12333     boolean player_is_standing_on_valid_field =
12334       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12335        (IS_WALKABLE(Feld[jx][jy]) &&
12336         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12337
12338     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12339       player->programmed_action = MV_DOWN;
12340   }
12341 }
12342
12343 /*
12344   MovePlayerOneStep()
12345   -----------------------------------------------------------------------------
12346   dx, dy:               direction (non-diagonal) to try to move the player to
12347   real_dx, real_dy:     direction as read from input device (can be diagonal)
12348 */
12349
12350 boolean MovePlayerOneStep(struct PlayerInfo *player,
12351                           int dx, int dy, int real_dx, int real_dy)
12352 {
12353   int jx = player->jx, jy = player->jy;
12354   int new_jx = jx + dx, new_jy = jy + dy;
12355   int can_move;
12356   boolean player_can_move = !player->cannot_move;
12357
12358   if (!player->active || (!dx && !dy))
12359     return MP_NO_ACTION;
12360
12361   player->MovDir = (dx < 0 ? MV_LEFT :
12362                     dx > 0 ? MV_RIGHT :
12363                     dy < 0 ? MV_UP :
12364                     dy > 0 ? MV_DOWN :  MV_NONE);
12365
12366   if (!IN_LEV_FIELD(new_jx, new_jy))
12367     return MP_NO_ACTION;
12368
12369   if (!player_can_move)
12370   {
12371     if (player->MovPos == 0)
12372     {
12373       player->is_moving = FALSE;
12374       player->is_digging = FALSE;
12375       player->is_collecting = FALSE;
12376       player->is_snapping = FALSE;
12377       player->is_pushing = FALSE;
12378     }
12379   }
12380
12381   if (!network.enabled && game.centered_player_nr == -1 &&
12382       !AllPlayersInSight(player, new_jx, new_jy))
12383     return MP_NO_ACTION;
12384
12385   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12386   if (can_move != MP_MOVING)
12387     return can_move;
12388
12389   // check if DigField() has caused relocation of the player
12390   if (player->jx != jx || player->jy != jy)
12391     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12392
12393   StorePlayer[jx][jy] = 0;
12394   player->last_jx = jx;
12395   player->last_jy = jy;
12396   player->jx = new_jx;
12397   player->jy = new_jy;
12398   StorePlayer[new_jx][new_jy] = player->element_nr;
12399
12400   if (player->move_delay_value_next != -1)
12401   {
12402     player->move_delay_value = player->move_delay_value_next;
12403     player->move_delay_value_next = -1;
12404   }
12405
12406   player->MovPos =
12407     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12408
12409   player->step_counter++;
12410
12411   PlayerVisit[jx][jy] = FrameCounter;
12412
12413   player->is_moving = TRUE;
12414
12415 #if 1
12416   // should better be called in MovePlayer(), but this breaks some tapes
12417   ScrollPlayer(player, SCROLL_INIT);
12418 #endif
12419
12420   return MP_MOVING;
12421 }
12422
12423 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12424 {
12425   int jx = player->jx, jy = player->jy;
12426   int old_jx = jx, old_jy = jy;
12427   int moved = MP_NO_ACTION;
12428
12429   if (!player->active)
12430     return FALSE;
12431
12432   if (!dx && !dy)
12433   {
12434     if (player->MovPos == 0)
12435     {
12436       player->is_moving = FALSE;
12437       player->is_digging = FALSE;
12438       player->is_collecting = FALSE;
12439       player->is_snapping = FALSE;
12440       player->is_pushing = FALSE;
12441     }
12442
12443     return FALSE;
12444   }
12445
12446   if (player->move_delay > 0)
12447     return FALSE;
12448
12449   player->move_delay = -1;              // set to "uninitialized" value
12450
12451   // store if player is automatically moved to next field
12452   player->is_auto_moving = (player->programmed_action != MV_NONE);
12453
12454   // remove the last programmed player action
12455   player->programmed_action = 0;
12456
12457   if (player->MovPos)
12458   {
12459     // should only happen if pre-1.2 tape recordings are played
12460     // this is only for backward compatibility
12461
12462     int original_move_delay_value = player->move_delay_value;
12463
12464 #if DEBUG
12465     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12466            tape.counter);
12467 #endif
12468
12469     // scroll remaining steps with finest movement resolution
12470     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12471
12472     while (player->MovPos)
12473     {
12474       ScrollPlayer(player, SCROLL_GO_ON);
12475       ScrollScreen(NULL, SCROLL_GO_ON);
12476
12477       AdvanceFrameAndPlayerCounters(player->index_nr);
12478
12479       DrawAllPlayers();
12480       BackToFront_WithFrameDelay(0);
12481     }
12482
12483     player->move_delay_value = original_move_delay_value;
12484   }
12485
12486   player->is_active = FALSE;
12487
12488   if (player->last_move_dir & MV_HORIZONTAL)
12489   {
12490     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12491       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12492   }
12493   else
12494   {
12495     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12496       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12497   }
12498
12499   if (!moved && !player->is_active)
12500   {
12501     player->is_moving = FALSE;
12502     player->is_digging = FALSE;
12503     player->is_collecting = FALSE;
12504     player->is_snapping = FALSE;
12505     player->is_pushing = FALSE;
12506   }
12507
12508   jx = player->jx;
12509   jy = player->jy;
12510
12511   if (moved & MP_MOVING && !ScreenMovPos &&
12512       (player->index_nr == game.centered_player_nr ||
12513        game.centered_player_nr == -1))
12514   {
12515     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12516
12517     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12518     {
12519       // actual player has left the screen -- scroll in that direction
12520       if (jx != old_jx)         // player has moved horizontally
12521         scroll_x += (jx - old_jx);
12522       else                      // player has moved vertically
12523         scroll_y += (jy - old_jy);
12524     }
12525     else
12526     {
12527       int offset = game.scroll_delay_value;
12528
12529       if (jx != old_jx)         // player has moved horizontally
12530       {
12531         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12532         int new_scroll_x = jx - MIDPOSX + offset_x;
12533
12534         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12535             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12536           scroll_x = new_scroll_x;
12537
12538         // don't scroll over playfield boundaries
12539         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12540
12541         // don't scroll more than one field at a time
12542         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12543
12544         // don't scroll against the player's moving direction
12545         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12546             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12547           scroll_x = old_scroll_x;
12548       }
12549       else                      // player has moved vertically
12550       {
12551         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12552         int new_scroll_y = jy - MIDPOSY + offset_y;
12553
12554         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12555             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12556           scroll_y = new_scroll_y;
12557
12558         // don't scroll over playfield boundaries
12559         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12560
12561         // don't scroll more than one field at a time
12562         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12563
12564         // don't scroll against the player's moving direction
12565         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12566             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12567           scroll_y = old_scroll_y;
12568       }
12569     }
12570
12571     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12572     {
12573       if (!network.enabled && game.centered_player_nr == -1 &&
12574           !AllPlayersInVisibleScreen())
12575       {
12576         scroll_x = old_scroll_x;
12577         scroll_y = old_scroll_y;
12578       }
12579       else
12580       {
12581         ScrollScreen(player, SCROLL_INIT);
12582         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12583       }
12584     }
12585   }
12586
12587   player->StepFrame = 0;
12588
12589   if (moved & MP_MOVING)
12590   {
12591     if (old_jx != jx && old_jy == jy)
12592       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12593     else if (old_jx == jx && old_jy != jy)
12594       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12595
12596     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12597
12598     player->last_move_dir = player->MovDir;
12599     player->is_moving = TRUE;
12600     player->is_snapping = FALSE;
12601     player->is_switching = FALSE;
12602     player->is_dropping = FALSE;
12603     player->is_dropping_pressed = FALSE;
12604     player->drop_pressed_delay = 0;
12605
12606 #if 0
12607     // should better be called here than above, but this breaks some tapes
12608     ScrollPlayer(player, SCROLL_INIT);
12609 #endif
12610   }
12611   else
12612   {
12613     CheckGravityMovementWhenNotMoving(player);
12614
12615     player->is_moving = FALSE;
12616
12617     /* at this point, the player is allowed to move, but cannot move right now
12618        (e.g. because of something blocking the way) -- ensure that the player
12619        is also allowed to move in the next frame (in old versions before 3.1.1,
12620        the player was forced to wait again for eight frames before next try) */
12621
12622     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12623       player->move_delay = 0;   // allow direct movement in the next frame
12624   }
12625
12626   if (player->move_delay == -1)         // not yet initialized by DigField()
12627     player->move_delay = player->move_delay_value;
12628
12629   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12630   {
12631     TestIfPlayerTouchesBadThing(jx, jy);
12632     TestIfPlayerTouchesCustomElement(jx, jy);
12633   }
12634
12635   if (!player->active)
12636     RemovePlayer(player);
12637
12638   return moved;
12639 }
12640
12641 void ScrollPlayer(struct PlayerInfo *player, int mode)
12642 {
12643   int jx = player->jx, jy = player->jy;
12644   int last_jx = player->last_jx, last_jy = player->last_jy;
12645   int move_stepsize = TILEX / player->move_delay_value;
12646
12647   if (!player->active)
12648     return;
12649
12650   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12651     return;
12652
12653   if (mode == SCROLL_INIT)
12654   {
12655     player->actual_frame_counter = FrameCounter;
12656     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12657
12658     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12659         Feld[last_jx][last_jy] == EL_EMPTY)
12660     {
12661       int last_field_block_delay = 0;   // start with no blocking at all
12662       int block_delay_adjustment = player->block_delay_adjustment;
12663
12664       // if player blocks last field, add delay for exactly one move
12665       if (player->block_last_field)
12666       {
12667         last_field_block_delay += player->move_delay_value;
12668
12669         // when blocking enabled, prevent moving up despite gravity
12670         if (player->gravity && player->MovDir == MV_UP)
12671           block_delay_adjustment = -1;
12672       }
12673
12674       // add block delay adjustment (also possible when not blocking)
12675       last_field_block_delay += block_delay_adjustment;
12676
12677       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12678       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12679     }
12680
12681     if (player->MovPos != 0)    // player has not yet reached destination
12682       return;
12683   }
12684   else if (!FrameReached(&player->actual_frame_counter, 1))
12685     return;
12686
12687   if (player->MovPos != 0)
12688   {
12689     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12690     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12691
12692     // before DrawPlayer() to draw correct player graphic for this case
12693     if (player->MovPos == 0)
12694       CheckGravityMovement(player);
12695   }
12696
12697   if (player->MovPos == 0)      // player reached destination field
12698   {
12699     if (player->move_delay_reset_counter > 0)
12700     {
12701       player->move_delay_reset_counter--;
12702
12703       if (player->move_delay_reset_counter == 0)
12704       {
12705         // continue with normal speed after quickly moving through gate
12706         HALVE_PLAYER_SPEED(player);
12707
12708         // be able to make the next move without delay
12709         player->move_delay = 0;
12710       }
12711     }
12712
12713     player->last_jx = jx;
12714     player->last_jy = jy;
12715
12716     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12717         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12718         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12719         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12720         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12721         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12722         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12723         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12724     {
12725       ExitPlayer(player);
12726
12727       if (game.players_still_needed == 0 &&
12728           (game.friends_still_needed == 0 ||
12729            IS_SP_ELEMENT(Feld[jx][jy])))
12730         LevelSolved();
12731     }
12732
12733     // this breaks one level: "machine", level 000
12734     {
12735       int move_direction = player->MovDir;
12736       int enter_side = MV_DIR_OPPOSITE(move_direction);
12737       int leave_side = move_direction;
12738       int old_jx = last_jx;
12739       int old_jy = last_jy;
12740       int old_element = Feld[old_jx][old_jy];
12741       int new_element = Feld[jx][jy];
12742
12743       if (IS_CUSTOM_ELEMENT(old_element))
12744         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12745                                    CE_LEFT_BY_PLAYER,
12746                                    player->index_bit, leave_side);
12747
12748       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12749                                           CE_PLAYER_LEAVES_X,
12750                                           player->index_bit, leave_side);
12751
12752       if (IS_CUSTOM_ELEMENT(new_element))
12753         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12754                                    player->index_bit, enter_side);
12755
12756       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12757                                           CE_PLAYER_ENTERS_X,
12758                                           player->index_bit, enter_side);
12759
12760       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12761                                         CE_MOVE_OF_X, move_direction);
12762     }
12763
12764     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12765     {
12766       TestIfPlayerTouchesBadThing(jx, jy);
12767       TestIfPlayerTouchesCustomElement(jx, jy);
12768
12769       /* needed because pushed element has not yet reached its destination,
12770          so it would trigger a change event at its previous field location */
12771       if (!player->is_pushing)
12772         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12773
12774       if (!player->active)
12775         RemovePlayer(player);
12776     }
12777
12778     if (!game.LevelSolved && level.use_step_counter)
12779     {
12780       int i;
12781
12782       TimePlayed++;
12783
12784       if (TimeLeft > 0)
12785       {
12786         TimeLeft--;
12787
12788         if (TimeLeft <= 10 && setup.time_limit)
12789           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12790
12791         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12792
12793         DisplayGameControlValues();
12794
12795         if (!TimeLeft && setup.time_limit)
12796           for (i = 0; i < MAX_PLAYERS; i++)
12797             KillPlayer(&stored_player[i]);
12798       }
12799       else if (game.no_time_limit && !game.all_players_gone)
12800       {
12801         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12802
12803         DisplayGameControlValues();
12804       }
12805     }
12806
12807     if (tape.single_step && tape.recording && !tape.pausing &&
12808         !player->programmed_action)
12809       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12810
12811     if (!player->programmed_action)
12812       CheckSaveEngineSnapshot(player);
12813   }
12814 }
12815
12816 void ScrollScreen(struct PlayerInfo *player, int mode)
12817 {
12818   static unsigned int screen_frame_counter = 0;
12819
12820   if (mode == SCROLL_INIT)
12821   {
12822     // set scrolling step size according to actual player's moving speed
12823     ScrollStepSize = TILEX / player->move_delay_value;
12824
12825     screen_frame_counter = FrameCounter;
12826     ScreenMovDir = player->MovDir;
12827     ScreenMovPos = player->MovPos;
12828     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12829     return;
12830   }
12831   else if (!FrameReached(&screen_frame_counter, 1))
12832     return;
12833
12834   if (ScreenMovPos)
12835   {
12836     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12837     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12838     redraw_mask |= REDRAW_FIELD;
12839   }
12840   else
12841     ScreenMovDir = MV_NONE;
12842 }
12843
12844 void TestIfPlayerTouchesCustomElement(int x, int y)
12845 {
12846   static int xy[4][2] =
12847   {
12848     { 0, -1 },
12849     { -1, 0 },
12850     { +1, 0 },
12851     { 0, +1 }
12852   };
12853   static int trigger_sides[4][2] =
12854   {
12855     // center side       border side
12856     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12857     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12858     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12859     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12860   };
12861   static int touch_dir[4] =
12862   {
12863     MV_LEFT | MV_RIGHT,
12864     MV_UP   | MV_DOWN,
12865     MV_UP   | MV_DOWN,
12866     MV_LEFT | MV_RIGHT
12867   };
12868   int center_element = Feld[x][y];      // should always be non-moving!
12869   int i;
12870
12871   for (i = 0; i < NUM_DIRECTIONS; i++)
12872   {
12873     int xx = x + xy[i][0];
12874     int yy = y + xy[i][1];
12875     int center_side = trigger_sides[i][0];
12876     int border_side = trigger_sides[i][1];
12877     int border_element;
12878
12879     if (!IN_LEV_FIELD(xx, yy))
12880       continue;
12881
12882     if (IS_PLAYER(x, y))                // player found at center element
12883     {
12884       struct PlayerInfo *player = PLAYERINFO(x, y);
12885
12886       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12887         border_element = Feld[xx][yy];          // may be moving!
12888       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12889         border_element = Feld[xx][yy];
12890       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12891         border_element = MovingOrBlocked2Element(xx, yy);
12892       else
12893         continue;               // center and border element do not touch
12894
12895       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12896                                  player->index_bit, border_side);
12897       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12898                                           CE_PLAYER_TOUCHES_X,
12899                                           player->index_bit, border_side);
12900
12901       {
12902         /* use player element that is initially defined in the level playfield,
12903            not the player element that corresponds to the runtime player number
12904            (example: a level that contains EL_PLAYER_3 as the only player would
12905            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12906         int player_element = PLAYERINFO(x, y)->initial_element;
12907
12908         CheckElementChangeBySide(xx, yy, border_element, player_element,
12909                                  CE_TOUCHING_X, border_side);
12910       }
12911     }
12912     else if (IS_PLAYER(xx, yy))         // player found at border element
12913     {
12914       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12915
12916       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12917       {
12918         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12919           continue;             // center and border element do not touch
12920       }
12921
12922       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12923                                  player->index_bit, center_side);
12924       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12925                                           CE_PLAYER_TOUCHES_X,
12926                                           player->index_bit, center_side);
12927
12928       {
12929         /* use player element that is initially defined in the level playfield,
12930            not the player element that corresponds to the runtime player number
12931            (example: a level that contains EL_PLAYER_3 as the only player would
12932            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12933         int player_element = PLAYERINFO(xx, yy)->initial_element;
12934
12935         CheckElementChangeBySide(x, y, center_element, player_element,
12936                                  CE_TOUCHING_X, center_side);
12937       }
12938
12939       break;
12940     }
12941   }
12942 }
12943
12944 void TestIfElementTouchesCustomElement(int x, int y)
12945 {
12946   static int xy[4][2] =
12947   {
12948     { 0, -1 },
12949     { -1, 0 },
12950     { +1, 0 },
12951     { 0, +1 }
12952   };
12953   static int trigger_sides[4][2] =
12954   {
12955     // center side      border side
12956     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12957     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12958     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12959     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12960   };
12961   static int touch_dir[4] =
12962   {
12963     MV_LEFT | MV_RIGHT,
12964     MV_UP   | MV_DOWN,
12965     MV_UP   | MV_DOWN,
12966     MV_LEFT | MV_RIGHT
12967   };
12968   boolean change_center_element = FALSE;
12969   int center_element = Feld[x][y];      // should always be non-moving!
12970   int border_element_old[NUM_DIRECTIONS];
12971   int i;
12972
12973   for (i = 0; i < NUM_DIRECTIONS; i++)
12974   {
12975     int xx = x + xy[i][0];
12976     int yy = y + xy[i][1];
12977     int border_element;
12978
12979     border_element_old[i] = -1;
12980
12981     if (!IN_LEV_FIELD(xx, yy))
12982       continue;
12983
12984     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12985       border_element = Feld[xx][yy];    // may be moving!
12986     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12987       border_element = Feld[xx][yy];
12988     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12989       border_element = MovingOrBlocked2Element(xx, yy);
12990     else
12991       continue;                 // center and border element do not touch
12992
12993     border_element_old[i] = border_element;
12994   }
12995
12996   for (i = 0; i < NUM_DIRECTIONS; i++)
12997   {
12998     int xx = x + xy[i][0];
12999     int yy = y + xy[i][1];
13000     int center_side = trigger_sides[i][0];
13001     int border_element = border_element_old[i];
13002
13003     if (border_element == -1)
13004       continue;
13005
13006     // check for change of border element
13007     CheckElementChangeBySide(xx, yy, border_element, center_element,
13008                              CE_TOUCHING_X, center_side);
13009
13010     // (center element cannot be player, so we dont have to check this here)
13011   }
13012
13013   for (i = 0; i < NUM_DIRECTIONS; i++)
13014   {
13015     int xx = x + xy[i][0];
13016     int yy = y + xy[i][1];
13017     int border_side = trigger_sides[i][1];
13018     int border_element = border_element_old[i];
13019
13020     if (border_element == -1)
13021       continue;
13022
13023     // check for change of center element (but change it only once)
13024     if (!change_center_element)
13025       change_center_element =
13026         CheckElementChangeBySide(x, y, center_element, border_element,
13027                                  CE_TOUCHING_X, border_side);
13028
13029     if (IS_PLAYER(xx, yy))
13030     {
13031       /* use player element that is initially defined in the level playfield,
13032          not the player element that corresponds to the runtime player number
13033          (example: a level that contains EL_PLAYER_3 as the only player would
13034          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13035       int player_element = PLAYERINFO(xx, yy)->initial_element;
13036
13037       CheckElementChangeBySide(x, y, center_element, player_element,
13038                                CE_TOUCHING_X, border_side);
13039     }
13040   }
13041 }
13042
13043 void TestIfElementHitsCustomElement(int x, int y, int direction)
13044 {
13045   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13046   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13047   int hitx = x + dx, hity = y + dy;
13048   int hitting_element = Feld[x][y];
13049   int touched_element;
13050
13051   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13052     return;
13053
13054   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13055                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13056
13057   if (IN_LEV_FIELD(hitx, hity))
13058   {
13059     int opposite_direction = MV_DIR_OPPOSITE(direction);
13060     int hitting_side = direction;
13061     int touched_side = opposite_direction;
13062     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13063                           MovDir[hitx][hity] != direction ||
13064                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13065
13066     object_hit = TRUE;
13067
13068     if (object_hit)
13069     {
13070       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13071                                CE_HITTING_X, touched_side);
13072
13073       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13074                                CE_HIT_BY_X, hitting_side);
13075
13076       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13077                                CE_HIT_BY_SOMETHING, opposite_direction);
13078
13079       if (IS_PLAYER(hitx, hity))
13080       {
13081         /* use player element that is initially defined in the level playfield,
13082            not the player element that corresponds to the runtime player number
13083            (example: a level that contains EL_PLAYER_3 as the only player would
13084            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13085         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13086
13087         CheckElementChangeBySide(x, y, hitting_element, player_element,
13088                                  CE_HITTING_X, touched_side);
13089       }
13090     }
13091   }
13092
13093   // "hitting something" is also true when hitting the playfield border
13094   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13095                            CE_HITTING_SOMETHING, direction);
13096 }
13097
13098 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13099 {
13100   int i, kill_x = -1, kill_y = -1;
13101
13102   int bad_element = -1;
13103   static int test_xy[4][2] =
13104   {
13105     { 0, -1 },
13106     { -1, 0 },
13107     { +1, 0 },
13108     { 0, +1 }
13109   };
13110   static int test_dir[4] =
13111   {
13112     MV_UP,
13113     MV_LEFT,
13114     MV_RIGHT,
13115     MV_DOWN
13116   };
13117
13118   for (i = 0; i < NUM_DIRECTIONS; i++)
13119   {
13120     int test_x, test_y, test_move_dir, test_element;
13121
13122     test_x = good_x + test_xy[i][0];
13123     test_y = good_y + test_xy[i][1];
13124
13125     if (!IN_LEV_FIELD(test_x, test_y))
13126       continue;
13127
13128     test_move_dir =
13129       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13130
13131     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13132
13133     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13134        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13135     */
13136     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13137         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13138     {
13139       kill_x = test_x;
13140       kill_y = test_y;
13141       bad_element = test_element;
13142
13143       break;
13144     }
13145   }
13146
13147   if (kill_x != -1 || kill_y != -1)
13148   {
13149     if (IS_PLAYER(good_x, good_y))
13150     {
13151       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13152
13153       if (player->shield_deadly_time_left > 0 &&
13154           !IS_INDESTRUCTIBLE(bad_element))
13155         Bang(kill_x, kill_y);
13156       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13157         KillPlayer(player);
13158     }
13159     else
13160       Bang(good_x, good_y);
13161   }
13162 }
13163
13164 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13165 {
13166   int i, kill_x = -1, kill_y = -1;
13167   int bad_element = Feld[bad_x][bad_y];
13168   static int test_xy[4][2] =
13169   {
13170     { 0, -1 },
13171     { -1, 0 },
13172     { +1, 0 },
13173     { 0, +1 }
13174   };
13175   static int touch_dir[4] =
13176   {
13177     MV_LEFT | MV_RIGHT,
13178     MV_UP   | MV_DOWN,
13179     MV_UP   | MV_DOWN,
13180     MV_LEFT | MV_RIGHT
13181   };
13182   static int test_dir[4] =
13183   {
13184     MV_UP,
13185     MV_LEFT,
13186     MV_RIGHT,
13187     MV_DOWN
13188   };
13189
13190   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13191     return;
13192
13193   for (i = 0; i < NUM_DIRECTIONS; i++)
13194   {
13195     int test_x, test_y, test_move_dir, test_element;
13196
13197     test_x = bad_x + test_xy[i][0];
13198     test_y = bad_y + test_xy[i][1];
13199
13200     if (!IN_LEV_FIELD(test_x, test_y))
13201       continue;
13202
13203     test_move_dir =
13204       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13205
13206     test_element = Feld[test_x][test_y];
13207
13208     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13209        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13210     */
13211     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13212         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13213     {
13214       // good thing is player or penguin that does not move away
13215       if (IS_PLAYER(test_x, test_y))
13216       {
13217         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13218
13219         if (bad_element == EL_ROBOT && player->is_moving)
13220           continue;     // robot does not kill player if he is moving
13221
13222         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13223         {
13224           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13225             continue;           // center and border element do not touch
13226         }
13227
13228         kill_x = test_x;
13229         kill_y = test_y;
13230
13231         break;
13232       }
13233       else if (test_element == EL_PENGUIN)
13234       {
13235         kill_x = test_x;
13236         kill_y = test_y;
13237
13238         break;
13239       }
13240     }
13241   }
13242
13243   if (kill_x != -1 || kill_y != -1)
13244   {
13245     if (IS_PLAYER(kill_x, kill_y))
13246     {
13247       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13248
13249       if (player->shield_deadly_time_left > 0 &&
13250           !IS_INDESTRUCTIBLE(bad_element))
13251         Bang(bad_x, bad_y);
13252       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13253         KillPlayer(player);
13254     }
13255     else
13256       Bang(kill_x, kill_y);
13257   }
13258 }
13259
13260 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13261 {
13262   int bad_element = Feld[bad_x][bad_y];
13263   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13264   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13265   int test_x = bad_x + dx, test_y = bad_y + dy;
13266   int test_move_dir, test_element;
13267   int kill_x = -1, kill_y = -1;
13268
13269   if (!IN_LEV_FIELD(test_x, test_y))
13270     return;
13271
13272   test_move_dir =
13273     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13274
13275   test_element = Feld[test_x][test_y];
13276
13277   if (test_move_dir != bad_move_dir)
13278   {
13279     // good thing can be player or penguin that does not move away
13280     if (IS_PLAYER(test_x, test_y))
13281     {
13282       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13283
13284       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13285          player as being hit when he is moving towards the bad thing, because
13286          the "get hit by" condition would be lost after the player stops) */
13287       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13288         return;         // player moves away from bad thing
13289
13290       kill_x = test_x;
13291       kill_y = test_y;
13292     }
13293     else if (test_element == EL_PENGUIN)
13294     {
13295       kill_x = test_x;
13296       kill_y = test_y;
13297     }
13298   }
13299
13300   if (kill_x != -1 || kill_y != -1)
13301   {
13302     if (IS_PLAYER(kill_x, kill_y))
13303     {
13304       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13305
13306       if (player->shield_deadly_time_left > 0 &&
13307           !IS_INDESTRUCTIBLE(bad_element))
13308         Bang(bad_x, bad_y);
13309       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13310         KillPlayer(player);
13311     }
13312     else
13313       Bang(kill_x, kill_y);
13314   }
13315 }
13316
13317 void TestIfPlayerTouchesBadThing(int x, int y)
13318 {
13319   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13320 }
13321
13322 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13323 {
13324   TestIfGoodThingHitsBadThing(x, y, move_dir);
13325 }
13326
13327 void TestIfBadThingTouchesPlayer(int x, int y)
13328 {
13329   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13330 }
13331
13332 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13333 {
13334   TestIfBadThingHitsGoodThing(x, y, move_dir);
13335 }
13336
13337 void TestIfFriendTouchesBadThing(int x, int y)
13338 {
13339   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13340 }
13341
13342 void TestIfBadThingTouchesFriend(int x, int y)
13343 {
13344   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13345 }
13346
13347 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13348 {
13349   int i, kill_x = bad_x, kill_y = bad_y;
13350   static int xy[4][2] =
13351   {
13352     { 0, -1 },
13353     { -1, 0 },
13354     { +1, 0 },
13355     { 0, +1 }
13356   };
13357
13358   for (i = 0; i < NUM_DIRECTIONS; i++)
13359   {
13360     int x, y, element;
13361
13362     x = bad_x + xy[i][0];
13363     y = bad_y + xy[i][1];
13364     if (!IN_LEV_FIELD(x, y))
13365       continue;
13366
13367     element = Feld[x][y];
13368     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13369         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13370     {
13371       kill_x = x;
13372       kill_y = y;
13373       break;
13374     }
13375   }
13376
13377   if (kill_x != bad_x || kill_y != bad_y)
13378     Bang(bad_x, bad_y);
13379 }
13380
13381 void KillPlayer(struct PlayerInfo *player)
13382 {
13383   int jx = player->jx, jy = player->jy;
13384
13385   if (!player->active)
13386     return;
13387
13388 #if 0
13389   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13390          player->killed, player->active, player->reanimated);
13391 #endif
13392
13393   /* the following code was introduced to prevent an infinite loop when calling
13394      -> Bang()
13395      -> CheckTriggeredElementChangeExt()
13396      -> ExecuteCustomElementAction()
13397      -> KillPlayer()
13398      -> (infinitely repeating the above sequence of function calls)
13399      which occurs when killing the player while having a CE with the setting
13400      "kill player X when explosion of <player X>"; the solution using a new
13401      field "player->killed" was chosen for backwards compatibility, although
13402      clever use of the fields "player->active" etc. would probably also work */
13403 #if 1
13404   if (player->killed)
13405     return;
13406 #endif
13407
13408   player->killed = TRUE;
13409
13410   // remove accessible field at the player's position
13411   Feld[jx][jy] = EL_EMPTY;
13412
13413   // deactivate shield (else Bang()/Explode() would not work right)
13414   player->shield_normal_time_left = 0;
13415   player->shield_deadly_time_left = 0;
13416
13417 #if 0
13418   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13419          player->killed, player->active, player->reanimated);
13420 #endif
13421
13422   Bang(jx, jy);
13423
13424 #if 0
13425   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13426          player->killed, player->active, player->reanimated);
13427 #endif
13428
13429   if (player->reanimated)       // killed player may have been reanimated
13430     player->killed = player->reanimated = FALSE;
13431   else
13432     BuryPlayer(player);
13433 }
13434
13435 static void KillPlayerUnlessEnemyProtected(int x, int y)
13436 {
13437   if (!PLAYER_ENEMY_PROTECTED(x, y))
13438     KillPlayer(PLAYERINFO(x, y));
13439 }
13440
13441 static void KillPlayerUnlessExplosionProtected(int x, int y)
13442 {
13443   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13444     KillPlayer(PLAYERINFO(x, y));
13445 }
13446
13447 void BuryPlayer(struct PlayerInfo *player)
13448 {
13449   int jx = player->jx, jy = player->jy;
13450
13451   if (!player->active)
13452     return;
13453
13454   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13455   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13456
13457   RemovePlayer(player);
13458
13459   player->buried = TRUE;
13460
13461   if (game.all_players_gone)
13462     game.GameOver = TRUE;
13463 }
13464
13465 void RemovePlayer(struct PlayerInfo *player)
13466 {
13467   int jx = player->jx, jy = player->jy;
13468   int i, found = FALSE;
13469
13470   player->present = FALSE;
13471   player->active = FALSE;
13472
13473   // required for some CE actions (even if the player is not active anymore)
13474   player->MovPos = 0;
13475
13476   if (!ExplodeField[jx][jy])
13477     StorePlayer[jx][jy] = 0;
13478
13479   if (player->is_moving)
13480     TEST_DrawLevelField(player->last_jx, player->last_jy);
13481
13482   for (i = 0; i < MAX_PLAYERS; i++)
13483     if (stored_player[i].active)
13484       found = TRUE;
13485
13486   if (!found)
13487   {
13488     game.all_players_gone = TRUE;
13489     game.GameOver = TRUE;
13490   }
13491
13492   game.exit_x = game.robot_wheel_x = jx;
13493   game.exit_y = game.robot_wheel_y = jy;
13494 }
13495
13496 void ExitPlayer(struct PlayerInfo *player)
13497 {
13498   DrawPlayer(player);   // needed here only to cleanup last field
13499   RemovePlayer(player);
13500
13501   if (game.players_still_needed > 0)
13502     game.players_still_needed--;
13503 }
13504
13505 static void setFieldForSnapping(int x, int y, int element, int direction)
13506 {
13507   struct ElementInfo *ei = &element_info[element];
13508   int direction_bit = MV_DIR_TO_BIT(direction);
13509   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13510   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13511                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13512
13513   Feld[x][y] = EL_ELEMENT_SNAPPING;
13514   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13515
13516   ResetGfxAnimation(x, y);
13517
13518   GfxElement[x][y] = element;
13519   GfxAction[x][y] = action;
13520   GfxDir[x][y] = direction;
13521   GfxFrame[x][y] = -1;
13522 }
13523
13524 /*
13525   =============================================================================
13526   checkDiagonalPushing()
13527   -----------------------------------------------------------------------------
13528   check if diagonal input device direction results in pushing of object
13529   (by checking if the alternative direction is walkable, diggable, ...)
13530   =============================================================================
13531 */
13532
13533 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13534                                     int x, int y, int real_dx, int real_dy)
13535 {
13536   int jx, jy, dx, dy, xx, yy;
13537
13538   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13539     return TRUE;
13540
13541   // diagonal direction: check alternative direction
13542   jx = player->jx;
13543   jy = player->jy;
13544   dx = x - jx;
13545   dy = y - jy;
13546   xx = jx + (dx == 0 ? real_dx : 0);
13547   yy = jy + (dy == 0 ? real_dy : 0);
13548
13549   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13550 }
13551
13552 /*
13553   =============================================================================
13554   DigField()
13555   -----------------------------------------------------------------------------
13556   x, y:                 field next to player (non-diagonal) to try to dig to
13557   real_dx, real_dy:     direction as read from input device (can be diagonal)
13558   =============================================================================
13559 */
13560
13561 static int DigField(struct PlayerInfo *player,
13562                     int oldx, int oldy, int x, int y,
13563                     int real_dx, int real_dy, int mode)
13564 {
13565   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13566   boolean player_was_pushing = player->is_pushing;
13567   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13568   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13569   int jx = oldx, jy = oldy;
13570   int dx = x - jx, dy = y - jy;
13571   int nextx = x + dx, nexty = y + dy;
13572   int move_direction = (dx == -1 ? MV_LEFT  :
13573                         dx == +1 ? MV_RIGHT :
13574                         dy == -1 ? MV_UP    :
13575                         dy == +1 ? MV_DOWN  : MV_NONE);
13576   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13577   int dig_side = MV_DIR_OPPOSITE(move_direction);
13578   int old_element = Feld[jx][jy];
13579   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13580   int collect_count;
13581
13582   if (is_player)                // function can also be called by EL_PENGUIN
13583   {
13584     if (player->MovPos == 0)
13585     {
13586       player->is_digging = FALSE;
13587       player->is_collecting = FALSE;
13588     }
13589
13590     if (player->MovPos == 0)    // last pushing move finished
13591       player->is_pushing = FALSE;
13592
13593     if (mode == DF_NO_PUSH)     // player just stopped pushing
13594     {
13595       player->is_switching = FALSE;
13596       player->push_delay = -1;
13597
13598       return MP_NO_ACTION;
13599     }
13600   }
13601
13602   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13603     old_element = Back[jx][jy];
13604
13605   // in case of element dropped at player position, check background
13606   else if (Back[jx][jy] != EL_EMPTY &&
13607            game.engine_version >= VERSION_IDENT(2,2,0,0))
13608     old_element = Back[jx][jy];
13609
13610   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13611     return MP_NO_ACTION;        // field has no opening in this direction
13612
13613   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13614     return MP_NO_ACTION;        // field has no opening in this direction
13615
13616   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13617   {
13618     SplashAcid(x, y);
13619
13620     Feld[jx][jy] = player->artwork_element;
13621     InitMovingField(jx, jy, MV_DOWN);
13622     Store[jx][jy] = EL_ACID;
13623     ContinueMoving(jx, jy);
13624     BuryPlayer(player);
13625
13626     return MP_DONT_RUN_INTO;
13627   }
13628
13629   if (player_can_move && DONT_RUN_INTO(element))
13630   {
13631     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13632
13633     return MP_DONT_RUN_INTO;
13634   }
13635
13636   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13637     return MP_NO_ACTION;
13638
13639   collect_count = element_info[element].collect_count_initial;
13640
13641   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13642     return MP_NO_ACTION;
13643
13644   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13645     player_can_move = player_can_move_or_snap;
13646
13647   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13648       game.engine_version >= VERSION_IDENT(2,2,0,0))
13649   {
13650     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13651                                player->index_bit, dig_side);
13652     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13653                                         player->index_bit, dig_side);
13654
13655     if (element == EL_DC_LANDMINE)
13656       Bang(x, y);
13657
13658     if (Feld[x][y] != element)          // field changed by snapping
13659       return MP_ACTION;
13660
13661     return MP_NO_ACTION;
13662   }
13663
13664   if (player->gravity && is_player && !player->is_auto_moving &&
13665       canFallDown(player) && move_direction != MV_DOWN &&
13666       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13667     return MP_NO_ACTION;        // player cannot walk here due to gravity
13668
13669   if (player_can_move &&
13670       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13671   {
13672     int sound_element = SND_ELEMENT(element);
13673     int sound_action = ACTION_WALKING;
13674
13675     if (IS_RND_GATE(element))
13676     {
13677       if (!player->key[RND_GATE_NR(element)])
13678         return MP_NO_ACTION;
13679     }
13680     else if (IS_RND_GATE_GRAY(element))
13681     {
13682       if (!player->key[RND_GATE_GRAY_NR(element)])
13683         return MP_NO_ACTION;
13684     }
13685     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13686     {
13687       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13688         return MP_NO_ACTION;
13689     }
13690     else if (element == EL_EXIT_OPEN ||
13691              element == EL_EM_EXIT_OPEN ||
13692              element == EL_EM_EXIT_OPENING ||
13693              element == EL_STEEL_EXIT_OPEN ||
13694              element == EL_EM_STEEL_EXIT_OPEN ||
13695              element == EL_EM_STEEL_EXIT_OPENING ||
13696              element == EL_SP_EXIT_OPEN ||
13697              element == EL_SP_EXIT_OPENING)
13698     {
13699       sound_action = ACTION_PASSING;    // player is passing exit
13700     }
13701     else if (element == EL_EMPTY)
13702     {
13703       sound_action = ACTION_MOVING;             // nothing to walk on
13704     }
13705
13706     // play sound from background or player, whatever is available
13707     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13708       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13709     else
13710       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13711   }
13712   else if (player_can_move &&
13713            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13714   {
13715     if (!ACCESS_FROM(element, opposite_direction))
13716       return MP_NO_ACTION;      // field not accessible from this direction
13717
13718     if (CAN_MOVE(element))      // only fixed elements can be passed!
13719       return MP_NO_ACTION;
13720
13721     if (IS_EM_GATE(element))
13722     {
13723       if (!player->key[EM_GATE_NR(element)])
13724         return MP_NO_ACTION;
13725     }
13726     else if (IS_EM_GATE_GRAY(element))
13727     {
13728       if (!player->key[EM_GATE_GRAY_NR(element)])
13729         return MP_NO_ACTION;
13730     }
13731     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13732     {
13733       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13734         return MP_NO_ACTION;
13735     }
13736     else if (IS_EMC_GATE(element))
13737     {
13738       if (!player->key[EMC_GATE_NR(element)])
13739         return MP_NO_ACTION;
13740     }
13741     else if (IS_EMC_GATE_GRAY(element))
13742     {
13743       if (!player->key[EMC_GATE_GRAY_NR(element)])
13744         return MP_NO_ACTION;
13745     }
13746     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13747     {
13748       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13749         return MP_NO_ACTION;
13750     }
13751     else if (element == EL_DC_GATE_WHITE ||
13752              element == EL_DC_GATE_WHITE_GRAY ||
13753              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13754     {
13755       if (player->num_white_keys == 0)
13756         return MP_NO_ACTION;
13757
13758       player->num_white_keys--;
13759     }
13760     else if (IS_SP_PORT(element))
13761     {
13762       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13763           element == EL_SP_GRAVITY_PORT_RIGHT ||
13764           element == EL_SP_GRAVITY_PORT_UP ||
13765           element == EL_SP_GRAVITY_PORT_DOWN)
13766         player->gravity = !player->gravity;
13767       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13768                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13769                element == EL_SP_GRAVITY_ON_PORT_UP ||
13770                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13771         player->gravity = TRUE;
13772       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13773                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13774                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13775                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13776         player->gravity = FALSE;
13777     }
13778
13779     // automatically move to the next field with double speed
13780     player->programmed_action = move_direction;
13781
13782     if (player->move_delay_reset_counter == 0)
13783     {
13784       player->move_delay_reset_counter = 2;     // two double speed steps
13785
13786       DOUBLE_PLAYER_SPEED(player);
13787     }
13788
13789     PlayLevelSoundAction(x, y, ACTION_PASSING);
13790   }
13791   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13792   {
13793     RemoveField(x, y);
13794
13795     if (mode != DF_SNAP)
13796     {
13797       GfxElement[x][y] = GFX_ELEMENT(element);
13798       player->is_digging = TRUE;
13799     }
13800
13801     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13802
13803     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13804                                         player->index_bit, dig_side);
13805
13806     if (mode == DF_SNAP)
13807     {
13808       if (level.block_snap_field)
13809         setFieldForSnapping(x, y, element, move_direction);
13810       else
13811         TestIfElementTouchesCustomElement(x, y);        // for empty space
13812
13813       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13814                                           player->index_bit, dig_side);
13815     }
13816   }
13817   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13818   {
13819     RemoveField(x, y);
13820
13821     if (is_player && mode != DF_SNAP)
13822     {
13823       GfxElement[x][y] = element;
13824       player->is_collecting = TRUE;
13825     }
13826
13827     if (element == EL_SPEED_PILL)
13828     {
13829       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13830     }
13831     else if (element == EL_EXTRA_TIME && level.time > 0)
13832     {
13833       TimeLeft += level.extra_time;
13834
13835       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13836
13837       DisplayGameControlValues();
13838     }
13839     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13840     {
13841       player->shield_normal_time_left += level.shield_normal_time;
13842       if (element == EL_SHIELD_DEADLY)
13843         player->shield_deadly_time_left += level.shield_deadly_time;
13844     }
13845     else if (element == EL_DYNAMITE ||
13846              element == EL_EM_DYNAMITE ||
13847              element == EL_SP_DISK_RED)
13848     {
13849       if (player->inventory_size < MAX_INVENTORY_SIZE)
13850         player->inventory_element[player->inventory_size++] = element;
13851
13852       DrawGameDoorValues();
13853     }
13854     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13855     {
13856       player->dynabomb_count++;
13857       player->dynabombs_left++;
13858     }
13859     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13860     {
13861       player->dynabomb_size++;
13862     }
13863     else if (element == EL_DYNABOMB_INCREASE_POWER)
13864     {
13865       player->dynabomb_xl = TRUE;
13866     }
13867     else if (IS_KEY(element))
13868     {
13869       player->key[KEY_NR(element)] = TRUE;
13870
13871       DrawGameDoorValues();
13872     }
13873     else if (element == EL_DC_KEY_WHITE)
13874     {
13875       player->num_white_keys++;
13876
13877       // display white keys?
13878       // DrawGameDoorValues();
13879     }
13880     else if (IS_ENVELOPE(element))
13881     {
13882       player->show_envelope = element;
13883     }
13884     else if (element == EL_EMC_LENSES)
13885     {
13886       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13887
13888       RedrawAllInvisibleElementsForLenses();
13889     }
13890     else if (element == EL_EMC_MAGNIFIER)
13891     {
13892       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13893
13894       RedrawAllInvisibleElementsForMagnifier();
13895     }
13896     else if (IS_DROPPABLE(element) ||
13897              IS_THROWABLE(element))     // can be collected and dropped
13898     {
13899       int i;
13900
13901       if (collect_count == 0)
13902         player->inventory_infinite_element = element;
13903       else
13904         for (i = 0; i < collect_count; i++)
13905           if (player->inventory_size < MAX_INVENTORY_SIZE)
13906             player->inventory_element[player->inventory_size++] = element;
13907
13908       DrawGameDoorValues();
13909     }
13910     else if (collect_count > 0)
13911     {
13912       game.gems_still_needed -= collect_count;
13913       if (game.gems_still_needed < 0)
13914         game.gems_still_needed = 0;
13915
13916       game.snapshot.collected_item = TRUE;
13917
13918       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13919
13920       DisplayGameControlValues();
13921     }
13922
13923     RaiseScoreElement(element);
13924     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13925
13926     if (is_player)
13927       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13928                                           player->index_bit, dig_side);
13929
13930     if (mode == DF_SNAP)
13931     {
13932       if (level.block_snap_field)
13933         setFieldForSnapping(x, y, element, move_direction);
13934       else
13935         TestIfElementTouchesCustomElement(x, y);        // for empty space
13936
13937       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13938                                           player->index_bit, dig_side);
13939     }
13940   }
13941   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13942   {
13943     if (mode == DF_SNAP && element != EL_BD_ROCK)
13944       return MP_NO_ACTION;
13945
13946     if (CAN_FALL(element) && dy)
13947       return MP_NO_ACTION;
13948
13949     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13950         !(element == EL_SPRING && level.use_spring_bug))
13951       return MP_NO_ACTION;
13952
13953     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13954         ((move_direction & MV_VERTICAL &&
13955           ((element_info[element].move_pattern & MV_LEFT &&
13956             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13957            (element_info[element].move_pattern & MV_RIGHT &&
13958             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13959          (move_direction & MV_HORIZONTAL &&
13960           ((element_info[element].move_pattern & MV_UP &&
13961             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13962            (element_info[element].move_pattern & MV_DOWN &&
13963             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13964       return MP_NO_ACTION;
13965
13966     // do not push elements already moving away faster than player
13967     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13968         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13969       return MP_NO_ACTION;
13970
13971     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13972     {
13973       if (player->push_delay_value == -1 || !player_was_pushing)
13974         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13975     }
13976     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13977     {
13978       if (player->push_delay_value == -1)
13979         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13980     }
13981     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13982     {
13983       if (!player->is_pushing)
13984         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13985     }
13986
13987     player->is_pushing = TRUE;
13988     player->is_active = TRUE;
13989
13990     if (!(IN_LEV_FIELD(nextx, nexty) &&
13991           (IS_FREE(nextx, nexty) ||
13992            (IS_SB_ELEMENT(element) &&
13993             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13994            (IS_CUSTOM_ELEMENT(element) &&
13995             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13996       return MP_NO_ACTION;
13997
13998     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13999       return MP_NO_ACTION;
14000
14001     if (player->push_delay == -1)       // new pushing; restart delay
14002       player->push_delay = 0;
14003
14004     if (player->push_delay < player->push_delay_value &&
14005         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14006         element != EL_SPRING && element != EL_BALLOON)
14007     {
14008       // make sure that there is no move delay before next try to push
14009       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14010         player->move_delay = 0;
14011
14012       return MP_NO_ACTION;
14013     }
14014
14015     if (IS_CUSTOM_ELEMENT(element) &&
14016         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14017     {
14018       if (!DigFieldByCE(nextx, nexty, element))
14019         return MP_NO_ACTION;
14020     }
14021
14022     if (IS_SB_ELEMENT(element))
14023     {
14024       boolean sokoban_task_solved = FALSE;
14025
14026       if (element == EL_SOKOBAN_FIELD_FULL)
14027       {
14028         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14029
14030         IncrementSokobanFieldsNeeded();
14031         IncrementSokobanObjectsNeeded();
14032       }
14033
14034       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14035       {
14036         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14037
14038         DecrementSokobanFieldsNeeded();
14039         DecrementSokobanObjectsNeeded();
14040
14041         // sokoban object was pushed from empty field to sokoban field
14042         if (Back[x][y] == EL_EMPTY)
14043           sokoban_task_solved = TRUE;
14044       }
14045
14046       Feld[x][y] = EL_SOKOBAN_OBJECT;
14047
14048       if (Back[x][y] == Back[nextx][nexty])
14049         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14050       else if (Back[x][y] != 0)
14051         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14052                                     ACTION_EMPTYING);
14053       else
14054         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14055                                     ACTION_FILLING);
14056
14057       if (sokoban_task_solved &&
14058           game.sokoban_fields_still_needed == 0 &&
14059           game.sokoban_objects_still_needed == 0 &&
14060           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14061       {
14062         game.players_still_needed = 0;
14063
14064         LevelSolved();
14065
14066         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14067       }
14068     }
14069     else
14070       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14071
14072     InitMovingField(x, y, move_direction);
14073     GfxAction[x][y] = ACTION_PUSHING;
14074
14075     if (mode == DF_SNAP)
14076       ContinueMoving(x, y);
14077     else
14078       MovPos[x][y] = (dx != 0 ? dx : dy);
14079
14080     Pushed[x][y] = TRUE;
14081     Pushed[nextx][nexty] = TRUE;
14082
14083     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14084       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14085     else
14086       player->push_delay_value = -1;    // get new value later
14087
14088     // check for element change _after_ element has been pushed
14089     if (game.use_change_when_pushing_bug)
14090     {
14091       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14092                                  player->index_bit, dig_side);
14093       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14094                                           player->index_bit, dig_side);
14095     }
14096   }
14097   else if (IS_SWITCHABLE(element))
14098   {
14099     if (PLAYER_SWITCHING(player, x, y))
14100     {
14101       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14102                                           player->index_bit, dig_side);
14103
14104       return MP_ACTION;
14105     }
14106
14107     player->is_switching = TRUE;
14108     player->switch_x = x;
14109     player->switch_y = y;
14110
14111     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14112
14113     if (element == EL_ROBOT_WHEEL)
14114     {
14115       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14116
14117       game.robot_wheel_x = x;
14118       game.robot_wheel_y = y;
14119       game.robot_wheel_active = TRUE;
14120
14121       TEST_DrawLevelField(x, y);
14122     }
14123     else if (element == EL_SP_TERMINAL)
14124     {
14125       int xx, yy;
14126
14127       SCAN_PLAYFIELD(xx, yy)
14128       {
14129         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14130         {
14131           Bang(xx, yy);
14132         }
14133         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14134         {
14135           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14136
14137           ResetGfxAnimation(xx, yy);
14138           TEST_DrawLevelField(xx, yy);
14139         }
14140       }
14141     }
14142     else if (IS_BELT_SWITCH(element))
14143     {
14144       ToggleBeltSwitch(x, y);
14145     }
14146     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14147              element == EL_SWITCHGATE_SWITCH_DOWN ||
14148              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14149              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14150     {
14151       ToggleSwitchgateSwitch(x, y);
14152     }
14153     else if (element == EL_LIGHT_SWITCH ||
14154              element == EL_LIGHT_SWITCH_ACTIVE)
14155     {
14156       ToggleLightSwitch(x, y);
14157     }
14158     else if (element == EL_TIMEGATE_SWITCH ||
14159              element == EL_DC_TIMEGATE_SWITCH)
14160     {
14161       ActivateTimegateSwitch(x, y);
14162     }
14163     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14164              element == EL_BALLOON_SWITCH_RIGHT ||
14165              element == EL_BALLOON_SWITCH_UP    ||
14166              element == EL_BALLOON_SWITCH_DOWN  ||
14167              element == EL_BALLOON_SWITCH_NONE  ||
14168              element == EL_BALLOON_SWITCH_ANY)
14169     {
14170       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14171                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14172                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14173                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14174                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14175                              move_direction);
14176     }
14177     else if (element == EL_LAMP)
14178     {
14179       Feld[x][y] = EL_LAMP_ACTIVE;
14180       game.lights_still_needed--;
14181
14182       ResetGfxAnimation(x, y);
14183       TEST_DrawLevelField(x, y);
14184     }
14185     else if (element == EL_TIME_ORB_FULL)
14186     {
14187       Feld[x][y] = EL_TIME_ORB_EMPTY;
14188
14189       if (level.time > 0 || level.use_time_orb_bug)
14190       {
14191         TimeLeft += level.time_orb_time;
14192         game.no_time_limit = FALSE;
14193
14194         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14195
14196         DisplayGameControlValues();
14197       }
14198
14199       ResetGfxAnimation(x, y);
14200       TEST_DrawLevelField(x, y);
14201     }
14202     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14203              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14204     {
14205       int xx, yy;
14206
14207       game.ball_state = !game.ball_state;
14208
14209       SCAN_PLAYFIELD(xx, yy)
14210       {
14211         int e = Feld[xx][yy];
14212
14213         if (game.ball_state)
14214         {
14215           if (e == EL_EMC_MAGIC_BALL)
14216             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14217           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14218             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14219         }
14220         else
14221         {
14222           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14223             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14224           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14225             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14226         }
14227       }
14228     }
14229
14230     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14231                                         player->index_bit, dig_side);
14232
14233     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14234                                         player->index_bit, dig_side);
14235
14236     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14237                                         player->index_bit, dig_side);
14238
14239     return MP_ACTION;
14240   }
14241   else
14242   {
14243     if (!PLAYER_SWITCHING(player, x, y))
14244     {
14245       player->is_switching = TRUE;
14246       player->switch_x = x;
14247       player->switch_y = y;
14248
14249       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14250                                  player->index_bit, dig_side);
14251       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14252                                           player->index_bit, dig_side);
14253
14254       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14255                                  player->index_bit, dig_side);
14256       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14257                                           player->index_bit, dig_side);
14258     }
14259
14260     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14261                                player->index_bit, dig_side);
14262     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14263                                         player->index_bit, dig_side);
14264
14265     return MP_NO_ACTION;
14266   }
14267
14268   player->push_delay = -1;
14269
14270   if (is_player)                // function can also be called by EL_PENGUIN
14271   {
14272     if (Feld[x][y] != element)          // really digged/collected something
14273     {
14274       player->is_collecting = !player->is_digging;
14275       player->is_active = TRUE;
14276     }
14277   }
14278
14279   return MP_MOVING;
14280 }
14281
14282 static boolean DigFieldByCE(int x, int y, int digging_element)
14283 {
14284   int element = Feld[x][y];
14285
14286   if (!IS_FREE(x, y))
14287   {
14288     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14289                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14290                   ACTION_BREAKING);
14291
14292     // no element can dig solid indestructible elements
14293     if (IS_INDESTRUCTIBLE(element) &&
14294         !IS_DIGGABLE(element) &&
14295         !IS_COLLECTIBLE(element))
14296       return FALSE;
14297
14298     if (AmoebaNr[x][y] &&
14299         (element == EL_AMOEBA_FULL ||
14300          element == EL_BD_AMOEBA ||
14301          element == EL_AMOEBA_GROWING))
14302     {
14303       AmoebaCnt[AmoebaNr[x][y]]--;
14304       AmoebaCnt2[AmoebaNr[x][y]]--;
14305     }
14306
14307     if (IS_MOVING(x, y))
14308       RemoveMovingField(x, y);
14309     else
14310     {
14311       RemoveField(x, y);
14312       TEST_DrawLevelField(x, y);
14313     }
14314
14315     // if digged element was about to explode, prevent the explosion
14316     ExplodeField[x][y] = EX_TYPE_NONE;
14317
14318     PlayLevelSoundAction(x, y, action);
14319   }
14320
14321   Store[x][y] = EL_EMPTY;
14322
14323   // this makes it possible to leave the removed element again
14324   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14325     Store[x][y] = element;
14326
14327   return TRUE;
14328 }
14329
14330 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14331 {
14332   int jx = player->jx, jy = player->jy;
14333   int x = jx + dx, y = jy + dy;
14334   int snap_direction = (dx == -1 ? MV_LEFT  :
14335                         dx == +1 ? MV_RIGHT :
14336                         dy == -1 ? MV_UP    :
14337                         dy == +1 ? MV_DOWN  : MV_NONE);
14338   boolean can_continue_snapping = (level.continuous_snapping &&
14339                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14340
14341   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14342     return FALSE;
14343
14344   if (!player->active || !IN_LEV_FIELD(x, y))
14345     return FALSE;
14346
14347   if (dx && dy)
14348     return FALSE;
14349
14350   if (!dx && !dy)
14351   {
14352     if (player->MovPos == 0)
14353       player->is_pushing = FALSE;
14354
14355     player->is_snapping = FALSE;
14356
14357     if (player->MovPos == 0)
14358     {
14359       player->is_moving = FALSE;
14360       player->is_digging = FALSE;
14361       player->is_collecting = FALSE;
14362     }
14363
14364     return FALSE;
14365   }
14366
14367   // prevent snapping with already pressed snap key when not allowed
14368   if (player->is_snapping && !can_continue_snapping)
14369     return FALSE;
14370
14371   player->MovDir = snap_direction;
14372
14373   if (player->MovPos == 0)
14374   {
14375     player->is_moving = FALSE;
14376     player->is_digging = FALSE;
14377     player->is_collecting = FALSE;
14378   }
14379
14380   player->is_dropping = FALSE;
14381   player->is_dropping_pressed = FALSE;
14382   player->drop_pressed_delay = 0;
14383
14384   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14385     return FALSE;
14386
14387   player->is_snapping = TRUE;
14388   player->is_active = TRUE;
14389
14390   if (player->MovPos == 0)
14391   {
14392     player->is_moving = FALSE;
14393     player->is_digging = FALSE;
14394     player->is_collecting = FALSE;
14395   }
14396
14397   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14398     TEST_DrawLevelField(player->last_jx, player->last_jy);
14399
14400   TEST_DrawLevelField(x, y);
14401
14402   return TRUE;
14403 }
14404
14405 static boolean DropElement(struct PlayerInfo *player)
14406 {
14407   int old_element, new_element;
14408   int dropx = player->jx, dropy = player->jy;
14409   int drop_direction = player->MovDir;
14410   int drop_side = drop_direction;
14411   int drop_element = get_next_dropped_element(player);
14412
14413   /* do not drop an element on top of another element; when holding drop key
14414      pressed without moving, dropped element must move away before the next
14415      element can be dropped (this is especially important if the next element
14416      is dynamite, which can be placed on background for historical reasons) */
14417   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14418     return MP_ACTION;
14419
14420   if (IS_THROWABLE(drop_element))
14421   {
14422     dropx += GET_DX_FROM_DIR(drop_direction);
14423     dropy += GET_DY_FROM_DIR(drop_direction);
14424
14425     if (!IN_LEV_FIELD(dropx, dropy))
14426       return FALSE;
14427   }
14428
14429   old_element = Feld[dropx][dropy];     // old element at dropping position
14430   new_element = drop_element;           // default: no change when dropping
14431
14432   // check if player is active, not moving and ready to drop
14433   if (!player->active || player->MovPos || player->drop_delay > 0)
14434     return FALSE;
14435
14436   // check if player has anything that can be dropped
14437   if (new_element == EL_UNDEFINED)
14438     return FALSE;
14439
14440   // only set if player has anything that can be dropped
14441   player->is_dropping_pressed = TRUE;
14442
14443   // check if drop key was pressed long enough for EM style dynamite
14444   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14445     return FALSE;
14446
14447   // check if anything can be dropped at the current position
14448   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14449     return FALSE;
14450
14451   // collected custom elements can only be dropped on empty fields
14452   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14453     return FALSE;
14454
14455   if (old_element != EL_EMPTY)
14456     Back[dropx][dropy] = old_element;   // store old element on this field
14457
14458   ResetGfxAnimation(dropx, dropy);
14459   ResetRandomAnimationValue(dropx, dropy);
14460
14461   if (player->inventory_size > 0 ||
14462       player->inventory_infinite_element != EL_UNDEFINED)
14463   {
14464     if (player->inventory_size > 0)
14465     {
14466       player->inventory_size--;
14467
14468       DrawGameDoorValues();
14469
14470       if (new_element == EL_DYNAMITE)
14471         new_element = EL_DYNAMITE_ACTIVE;
14472       else if (new_element == EL_EM_DYNAMITE)
14473         new_element = EL_EM_DYNAMITE_ACTIVE;
14474       else if (new_element == EL_SP_DISK_RED)
14475         new_element = EL_SP_DISK_RED_ACTIVE;
14476     }
14477
14478     Feld[dropx][dropy] = new_element;
14479
14480     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14481       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14482                           el2img(Feld[dropx][dropy]), 0);
14483
14484     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14485
14486     // needed if previous element just changed to "empty" in the last frame
14487     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14488
14489     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14490                                player->index_bit, drop_side);
14491     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14492                                         CE_PLAYER_DROPS_X,
14493                                         player->index_bit, drop_side);
14494
14495     TestIfElementTouchesCustomElement(dropx, dropy);
14496   }
14497   else          // player is dropping a dyna bomb
14498   {
14499     player->dynabombs_left--;
14500
14501     Feld[dropx][dropy] = new_element;
14502
14503     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14504       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14505                           el2img(Feld[dropx][dropy]), 0);
14506
14507     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14508   }
14509
14510   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14511     InitField_WithBug1(dropx, dropy, FALSE);
14512
14513   new_element = Feld[dropx][dropy];     // element might have changed
14514
14515   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14516       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14517   {
14518     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14519       MovDir[dropx][dropy] = drop_direction;
14520
14521     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14522
14523     // do not cause impact style collision by dropping elements that can fall
14524     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14525   }
14526
14527   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14528   player->is_dropping = TRUE;
14529
14530   player->drop_pressed_delay = 0;
14531   player->is_dropping_pressed = FALSE;
14532
14533   player->drop_x = dropx;
14534   player->drop_y = dropy;
14535
14536   return TRUE;
14537 }
14538
14539 // ----------------------------------------------------------------------------
14540 // game sound playing functions
14541 // ----------------------------------------------------------------------------
14542
14543 static int *loop_sound_frame = NULL;
14544 static int *loop_sound_volume = NULL;
14545
14546 void InitPlayLevelSound(void)
14547 {
14548   int num_sounds = getSoundListSize();
14549
14550   checked_free(loop_sound_frame);
14551   checked_free(loop_sound_volume);
14552
14553   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14554   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14555 }
14556
14557 static void PlayLevelSound(int x, int y, int nr)
14558 {
14559   int sx = SCREENX(x), sy = SCREENY(y);
14560   int volume, stereo_position;
14561   int max_distance = 8;
14562   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14563
14564   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14565       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14566     return;
14567
14568   if (!IN_LEV_FIELD(x, y) ||
14569       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14570       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14571     return;
14572
14573   volume = SOUND_MAX_VOLUME;
14574
14575   if (!IN_SCR_FIELD(sx, sy))
14576   {
14577     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14578     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14579
14580     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14581   }
14582
14583   stereo_position = (SOUND_MAX_LEFT +
14584                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14585                      (SCR_FIELDX + 2 * max_distance));
14586
14587   if (IS_LOOP_SOUND(nr))
14588   {
14589     /* This assures that quieter loop sounds do not overwrite louder ones,
14590        while restarting sound volume comparison with each new game frame. */
14591
14592     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14593       return;
14594
14595     loop_sound_volume[nr] = volume;
14596     loop_sound_frame[nr] = FrameCounter;
14597   }
14598
14599   PlaySoundExt(nr, volume, stereo_position, type);
14600 }
14601
14602 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14603 {
14604   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14605                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14606                  y < LEVELY(BY1) ? LEVELY(BY1) :
14607                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14608                  sound_action);
14609 }
14610
14611 static void PlayLevelSoundAction(int x, int y, int action)
14612 {
14613   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14614 }
14615
14616 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14617 {
14618   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14619
14620   if (sound_effect != SND_UNDEFINED)
14621     PlayLevelSound(x, y, sound_effect);
14622 }
14623
14624 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14625                                               int action)
14626 {
14627   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14628
14629   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14630     PlayLevelSound(x, y, sound_effect);
14631 }
14632
14633 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14634 {
14635   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14636
14637   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14638     PlayLevelSound(x, y, sound_effect);
14639 }
14640
14641 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14642 {
14643   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14644
14645   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14646     StopSound(sound_effect);
14647 }
14648
14649 static int getLevelMusicNr(void)
14650 {
14651   if (levelset.music[level_nr] != MUS_UNDEFINED)
14652     return levelset.music[level_nr];            // from config file
14653   else
14654     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14655 }
14656
14657 static void FadeLevelSounds(void)
14658 {
14659   FadeSounds();
14660 }
14661
14662 static void FadeLevelMusic(void)
14663 {
14664   int music_nr = getLevelMusicNr();
14665   char *curr_music = getCurrentlyPlayingMusicFilename();
14666   char *next_music = getMusicInfoEntryFilename(music_nr);
14667
14668   if (!strEqual(curr_music, next_music))
14669     FadeMusic();
14670 }
14671
14672 void FadeLevelSoundsAndMusic(void)
14673 {
14674   FadeLevelSounds();
14675   FadeLevelMusic();
14676 }
14677
14678 static void PlayLevelMusic(void)
14679 {
14680   int music_nr = getLevelMusicNr();
14681   char *curr_music = getCurrentlyPlayingMusicFilename();
14682   char *next_music = getMusicInfoEntryFilename(music_nr);
14683
14684   if (!strEqual(curr_music, next_music))
14685     PlayMusicLoop(music_nr);
14686 }
14687
14688 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14689 {
14690   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14691   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14692   int x = xx - 1 - offset;
14693   int y = yy - 1 - offset;
14694
14695   switch (sample)
14696   {
14697     case SAMPLE_blank:
14698       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14699       break;
14700
14701     case SAMPLE_roll:
14702       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14703       break;
14704
14705     case SAMPLE_stone:
14706       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14707       break;
14708
14709     case SAMPLE_nut:
14710       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14711       break;
14712
14713     case SAMPLE_crack:
14714       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14715       break;
14716
14717     case SAMPLE_bug:
14718       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14719       break;
14720
14721     case SAMPLE_tank:
14722       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14723       break;
14724
14725     case SAMPLE_android_clone:
14726       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14727       break;
14728
14729     case SAMPLE_android_move:
14730       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14731       break;
14732
14733     case SAMPLE_spring:
14734       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14735       break;
14736
14737     case SAMPLE_slurp:
14738       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14739       break;
14740
14741     case SAMPLE_eater:
14742       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14743       break;
14744
14745     case SAMPLE_eater_eat:
14746       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14747       break;
14748
14749     case SAMPLE_alien:
14750       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14751       break;
14752
14753     case SAMPLE_collect:
14754       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14755       break;
14756
14757     case SAMPLE_diamond:
14758       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14759       break;
14760
14761     case SAMPLE_squash:
14762       // !!! CHECK THIS !!!
14763 #if 1
14764       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14765 #else
14766       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14767 #endif
14768       break;
14769
14770     case SAMPLE_wonderfall:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14772       break;
14773
14774     case SAMPLE_drip:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14776       break;
14777
14778     case SAMPLE_push:
14779       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14780       break;
14781
14782     case SAMPLE_dirt:
14783       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14784       break;
14785
14786     case SAMPLE_acid:
14787       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14788       break;
14789
14790     case SAMPLE_ball:
14791       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14792       break;
14793
14794     case SAMPLE_grow:
14795       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14796       break;
14797
14798     case SAMPLE_wonder:
14799       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14800       break;
14801
14802     case SAMPLE_door:
14803       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14804       break;
14805
14806     case SAMPLE_exit_open:
14807       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14808       break;
14809
14810     case SAMPLE_exit_leave:
14811       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14812       break;
14813
14814     case SAMPLE_dynamite:
14815       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14816       break;
14817
14818     case SAMPLE_tick:
14819       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14820       break;
14821
14822     case SAMPLE_press:
14823       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14824       break;
14825
14826     case SAMPLE_wheel:
14827       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14828       break;
14829
14830     case SAMPLE_boom:
14831       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14832       break;
14833
14834     case SAMPLE_die:
14835       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14836       break;
14837
14838     case SAMPLE_time:
14839       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14840       break;
14841
14842     default:
14843       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14844       break;
14845   }
14846 }
14847
14848 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14849 {
14850   int element = map_element_SP_to_RND(element_sp);
14851   int action = map_action_SP_to_RND(action_sp);
14852   int offset = (setup.sp_show_border_elements ? 0 : 1);
14853   int x = xx - offset;
14854   int y = yy - offset;
14855
14856   PlayLevelSoundElementAction(x, y, element, action);
14857 }
14858
14859 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14860 {
14861   int element = map_element_MM_to_RND(element_mm);
14862   int action = map_action_MM_to_RND(action_mm);
14863   int offset = 0;
14864   int x = xx - offset;
14865   int y = yy - offset;
14866
14867   if (!IS_MM_ELEMENT(element))
14868     element = EL_MM_DEFAULT;
14869
14870   PlayLevelSoundElementAction(x, y, element, action);
14871 }
14872
14873 void PlaySound_MM(int sound_mm)
14874 {
14875   int sound = map_sound_MM_to_RND(sound_mm);
14876
14877   if (sound == SND_UNDEFINED)
14878     return;
14879
14880   PlaySound(sound);
14881 }
14882
14883 void PlaySoundLoop_MM(int sound_mm)
14884 {
14885   int sound = map_sound_MM_to_RND(sound_mm);
14886
14887   if (sound == SND_UNDEFINED)
14888     return;
14889
14890   PlaySoundLoop(sound);
14891 }
14892
14893 void StopSound_MM(int sound_mm)
14894 {
14895   int sound = map_sound_MM_to_RND(sound_mm);
14896
14897   if (sound == SND_UNDEFINED)
14898     return;
14899
14900   StopSound(sound);
14901 }
14902
14903 void RaiseScore(int value)
14904 {
14905   game.score += value;
14906
14907   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14908
14909   DisplayGameControlValues();
14910 }
14911
14912 void RaiseScoreElement(int element)
14913 {
14914   switch (element)
14915   {
14916     case EL_EMERALD:
14917     case EL_BD_DIAMOND:
14918     case EL_EMERALD_YELLOW:
14919     case EL_EMERALD_RED:
14920     case EL_EMERALD_PURPLE:
14921     case EL_SP_INFOTRON:
14922       RaiseScore(level.score[SC_EMERALD]);
14923       break;
14924     case EL_DIAMOND:
14925       RaiseScore(level.score[SC_DIAMOND]);
14926       break;
14927     case EL_CRYSTAL:
14928       RaiseScore(level.score[SC_CRYSTAL]);
14929       break;
14930     case EL_PEARL:
14931       RaiseScore(level.score[SC_PEARL]);
14932       break;
14933     case EL_BUG:
14934     case EL_BD_BUTTERFLY:
14935     case EL_SP_ELECTRON:
14936       RaiseScore(level.score[SC_BUG]);
14937       break;
14938     case EL_SPACESHIP:
14939     case EL_BD_FIREFLY:
14940     case EL_SP_SNIKSNAK:
14941       RaiseScore(level.score[SC_SPACESHIP]);
14942       break;
14943     case EL_YAMYAM:
14944     case EL_DARK_YAMYAM:
14945       RaiseScore(level.score[SC_YAMYAM]);
14946       break;
14947     case EL_ROBOT:
14948       RaiseScore(level.score[SC_ROBOT]);
14949       break;
14950     case EL_PACMAN:
14951       RaiseScore(level.score[SC_PACMAN]);
14952       break;
14953     case EL_NUT:
14954       RaiseScore(level.score[SC_NUT]);
14955       break;
14956     case EL_DYNAMITE:
14957     case EL_EM_DYNAMITE:
14958     case EL_SP_DISK_RED:
14959     case EL_DYNABOMB_INCREASE_NUMBER:
14960     case EL_DYNABOMB_INCREASE_SIZE:
14961     case EL_DYNABOMB_INCREASE_POWER:
14962       RaiseScore(level.score[SC_DYNAMITE]);
14963       break;
14964     case EL_SHIELD_NORMAL:
14965     case EL_SHIELD_DEADLY:
14966       RaiseScore(level.score[SC_SHIELD]);
14967       break;
14968     case EL_EXTRA_TIME:
14969       RaiseScore(level.extra_time_score);
14970       break;
14971     case EL_KEY_1:
14972     case EL_KEY_2:
14973     case EL_KEY_3:
14974     case EL_KEY_4:
14975     case EL_EM_KEY_1:
14976     case EL_EM_KEY_2:
14977     case EL_EM_KEY_3:
14978     case EL_EM_KEY_4:
14979     case EL_EMC_KEY_5:
14980     case EL_EMC_KEY_6:
14981     case EL_EMC_KEY_7:
14982     case EL_EMC_KEY_8:
14983     case EL_DC_KEY_WHITE:
14984       RaiseScore(level.score[SC_KEY]);
14985       break;
14986     default:
14987       RaiseScore(element_info[element].collect_score);
14988       break;
14989   }
14990 }
14991
14992 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14993 {
14994   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14995   {
14996     // closing door required in case of envelope style request dialogs
14997     if (!skip_request)
14998     {
14999       // prevent short reactivation of overlay buttons while closing door
15000       SetOverlayActive(FALSE);
15001
15002       CloseDoor(DOOR_CLOSE_1);
15003     }
15004
15005     if (network.enabled)
15006       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15007     else
15008     {
15009       if (quick_quit)
15010         FadeSkipNextFadeIn();
15011
15012       SetGameStatus(GAME_MODE_MAIN);
15013
15014       DrawMainMenu();
15015     }
15016   }
15017   else          // continue playing the game
15018   {
15019     if (tape.playing && tape.deactivate_display)
15020       TapeDeactivateDisplayOff(TRUE);
15021
15022     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15023
15024     if (tape.playing && tape.deactivate_display)
15025       TapeDeactivateDisplayOn();
15026   }
15027 }
15028
15029 void RequestQuitGame(boolean ask_if_really_quit)
15030 {
15031   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15032   boolean skip_request = game.all_players_gone || quick_quit;
15033
15034   RequestQuitGameExt(skip_request, quick_quit,
15035                      "Do you really want to quit the game?");
15036 }
15037
15038 void RequestRestartGame(char *message)
15039 {
15040   game.restart_game_message = NULL;
15041
15042   boolean has_started_game = hasStartedNetworkGame();
15043   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15044
15045   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15046   {
15047     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15048   }
15049   else
15050   {
15051     SetGameStatus(GAME_MODE_MAIN);
15052
15053     DrawMainMenu();
15054   }
15055 }
15056
15057 void CheckGameOver(void)
15058 {
15059   static boolean last_game_over = FALSE;
15060   static int game_over_delay = 0;
15061   int game_over_delay_value = 50;
15062   boolean game_over = checkGameFailed();
15063
15064   // do not handle game over if request dialog is already active
15065   if (game.request_active)
15066     return;
15067
15068   // do not ask to play again if game was never actually played
15069   if (!game.GamePlayed)
15070     return;
15071
15072   if (!game_over)
15073   {
15074     last_game_over = FALSE;
15075     game_over_delay = game_over_delay_value;
15076
15077     return;
15078   }
15079
15080   if (game_over_delay > 0)
15081   {
15082     game_over_delay--;
15083
15084     return;
15085   }
15086
15087   if (last_game_over != game_over)
15088     game.restart_game_message = (hasStartedNetworkGame() ?
15089                                  "Game over! Play it again?" :
15090                                  "Game over!");
15091
15092   last_game_over = game_over;
15093 }
15094
15095 boolean checkGameSolved(void)
15096 {
15097   // set for all game engines if level was solved
15098   return game.LevelSolved_GameEnd;
15099 }
15100
15101 boolean checkGameFailed(void)
15102 {
15103   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15104     return (game_em.game_over && !game_em.level_solved);
15105   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15106     return (game_sp.game_over && !game_sp.level_solved);
15107   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15108     return (game_mm.game_over && !game_mm.level_solved);
15109   else                          // GAME_ENGINE_TYPE_RND
15110     return (game.GameOver && !game.LevelSolved);
15111 }
15112
15113 boolean checkGameEnded(void)
15114 {
15115   return (checkGameSolved() || checkGameFailed());
15116 }
15117
15118
15119 // ----------------------------------------------------------------------------
15120 // random generator functions
15121 // ----------------------------------------------------------------------------
15122
15123 unsigned int InitEngineRandom_RND(int seed)
15124 {
15125   game.num_random_calls = 0;
15126
15127   return InitEngineRandom(seed);
15128 }
15129
15130 unsigned int RND(int max)
15131 {
15132   if (max > 0)
15133   {
15134     game.num_random_calls++;
15135
15136     return GetEngineRandom(max);
15137   }
15138
15139   return 0;
15140 }
15141
15142
15143 // ----------------------------------------------------------------------------
15144 // game engine snapshot handling functions
15145 // ----------------------------------------------------------------------------
15146
15147 struct EngineSnapshotInfo
15148 {
15149   // runtime values for custom element collect score
15150   int collect_score[NUM_CUSTOM_ELEMENTS];
15151
15152   // runtime values for group element choice position
15153   int choice_pos[NUM_GROUP_ELEMENTS];
15154
15155   // runtime values for belt position animations
15156   int belt_graphic[4][NUM_BELT_PARTS];
15157   int belt_anim_mode[4][NUM_BELT_PARTS];
15158 };
15159
15160 static struct EngineSnapshotInfo engine_snapshot_rnd;
15161 static char *snapshot_level_identifier = NULL;
15162 static int snapshot_level_nr = -1;
15163
15164 static void SaveEngineSnapshotValues_RND(void)
15165 {
15166   static int belt_base_active_element[4] =
15167   {
15168     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15169     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15170     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15171     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15172   };
15173   int i, j;
15174
15175   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15176   {
15177     int element = EL_CUSTOM_START + i;
15178
15179     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15180   }
15181
15182   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15183   {
15184     int element = EL_GROUP_START + i;
15185
15186     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15187   }
15188
15189   for (i = 0; i < 4; i++)
15190   {
15191     for (j = 0; j < NUM_BELT_PARTS; j++)
15192     {
15193       int element = belt_base_active_element[i] + j;
15194       int graphic = el2img(element);
15195       int anim_mode = graphic_info[graphic].anim_mode;
15196
15197       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15198       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15199     }
15200   }
15201 }
15202
15203 static void LoadEngineSnapshotValues_RND(void)
15204 {
15205   unsigned int num_random_calls = game.num_random_calls;
15206   int i, j;
15207
15208   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15209   {
15210     int element = EL_CUSTOM_START + i;
15211
15212     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15213   }
15214
15215   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15216   {
15217     int element = EL_GROUP_START + i;
15218
15219     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15220   }
15221
15222   for (i = 0; i < 4; i++)
15223   {
15224     for (j = 0; j < NUM_BELT_PARTS; j++)
15225     {
15226       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15227       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15228
15229       graphic_info[graphic].anim_mode = anim_mode;
15230     }
15231   }
15232
15233   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15234   {
15235     InitRND(tape.random_seed);
15236     for (i = 0; i < num_random_calls; i++)
15237       RND(1);
15238   }
15239
15240   if (game.num_random_calls != num_random_calls)
15241   {
15242     Error(ERR_INFO, "number of random calls out of sync");
15243     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15244     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15245     Error(ERR_EXIT, "this should not happen -- please debug");
15246   }
15247 }
15248
15249 void FreeEngineSnapshotSingle(void)
15250 {
15251   FreeSnapshotSingle();
15252
15253   setString(&snapshot_level_identifier, NULL);
15254   snapshot_level_nr = -1;
15255 }
15256
15257 void FreeEngineSnapshotList(void)
15258 {
15259   FreeSnapshotList();
15260 }
15261
15262 static ListNode *SaveEngineSnapshotBuffers(void)
15263 {
15264   ListNode *buffers = NULL;
15265
15266   // copy some special values to a structure better suited for the snapshot
15267
15268   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15269     SaveEngineSnapshotValues_RND();
15270   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15271     SaveEngineSnapshotValues_EM();
15272   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15273     SaveEngineSnapshotValues_SP(&buffers);
15274   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15275     SaveEngineSnapshotValues_MM(&buffers);
15276
15277   // save values stored in special snapshot structure
15278
15279   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15280     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15281   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15282     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15283   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15284     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15285   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15286     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15287
15288   // save further RND engine values
15289
15290   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15291   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15293
15294   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15295   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15296   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15297   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15298   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15299
15300   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15301   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15302   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15303
15304   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15305
15306   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15307   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15308
15309   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15310   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15311   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15312   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15313   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15314   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15315   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15316   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15317   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15318   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15319   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15320   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15321   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15322   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15323   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15324   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15325   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15326   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15327
15328   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15329   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15330
15331   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15332   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15333   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15334
15335   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15336   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15337
15338   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15339   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15340   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15341   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15342   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15343
15344   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15345   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15346
15347 #if 0
15348   ListNode *node = engine_snapshot_list_rnd;
15349   int num_bytes = 0;
15350
15351   while (node != NULL)
15352   {
15353     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15354
15355     node = node->next;
15356   }
15357
15358   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15359 #endif
15360
15361   return buffers;
15362 }
15363
15364 void SaveEngineSnapshotSingle(void)
15365 {
15366   ListNode *buffers = SaveEngineSnapshotBuffers();
15367
15368   // finally save all snapshot buffers to single snapshot
15369   SaveSnapshotSingle(buffers);
15370
15371   // save level identification information
15372   setString(&snapshot_level_identifier, leveldir_current->identifier);
15373   snapshot_level_nr = level_nr;
15374 }
15375
15376 boolean CheckSaveEngineSnapshotToList(void)
15377 {
15378   boolean save_snapshot =
15379     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15380      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15381       game.snapshot.changed_action) ||
15382      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15383       game.snapshot.collected_item));
15384
15385   game.snapshot.changed_action = FALSE;
15386   game.snapshot.collected_item = FALSE;
15387   game.snapshot.save_snapshot = save_snapshot;
15388
15389   return save_snapshot;
15390 }
15391
15392 void SaveEngineSnapshotToList(void)
15393 {
15394   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15395       tape.quick_resume)
15396     return;
15397
15398   ListNode *buffers = SaveEngineSnapshotBuffers();
15399
15400   // finally save all snapshot buffers to snapshot list
15401   SaveSnapshotToList(buffers);
15402 }
15403
15404 void SaveEngineSnapshotToListInitial(void)
15405 {
15406   FreeEngineSnapshotList();
15407
15408   SaveEngineSnapshotToList();
15409 }
15410
15411 static void LoadEngineSnapshotValues(void)
15412 {
15413   // restore special values from snapshot structure
15414
15415   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15416     LoadEngineSnapshotValues_RND();
15417   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15418     LoadEngineSnapshotValues_EM();
15419   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15420     LoadEngineSnapshotValues_SP();
15421   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15422     LoadEngineSnapshotValues_MM();
15423 }
15424
15425 void LoadEngineSnapshotSingle(void)
15426 {
15427   LoadSnapshotSingle();
15428
15429   LoadEngineSnapshotValues();
15430 }
15431
15432 static void LoadEngineSnapshot_Undo(int steps)
15433 {
15434   LoadSnapshotFromList_Older(steps);
15435
15436   LoadEngineSnapshotValues();
15437 }
15438
15439 static void LoadEngineSnapshot_Redo(int steps)
15440 {
15441   LoadSnapshotFromList_Newer(steps);
15442
15443   LoadEngineSnapshotValues();
15444 }
15445
15446 boolean CheckEngineSnapshotSingle(void)
15447 {
15448   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15449           snapshot_level_nr == level_nr);
15450 }
15451
15452 boolean CheckEngineSnapshotList(void)
15453 {
15454   return CheckSnapshotList();
15455 }
15456
15457
15458 // ---------- new game button stuff -------------------------------------------
15459
15460 static struct
15461 {
15462   int graphic;
15463   struct XY *pos;
15464   int gadget_id;
15465   boolean *setup_value;
15466   boolean allowed_on_tape;
15467   char *infotext;
15468 } gamebutton_info[NUM_GAME_BUTTONS] =
15469 {
15470   {
15471     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15472     GAME_CTRL_ID_STOP,                          NULL,
15473     TRUE,                                       "stop game"
15474   },
15475   {
15476     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15477     GAME_CTRL_ID_PAUSE,                         NULL,
15478     TRUE,                                       "pause game"
15479   },
15480   {
15481     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15482     GAME_CTRL_ID_PLAY,                          NULL,
15483     TRUE,                                       "play game"
15484   },
15485   {
15486     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15487     GAME_CTRL_ID_UNDO,                          NULL,
15488     TRUE,                                       "undo step"
15489   },
15490   {
15491     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15492     GAME_CTRL_ID_REDO,                          NULL,
15493     TRUE,                                       "redo step"
15494   },
15495   {
15496     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15497     GAME_CTRL_ID_SAVE,                          NULL,
15498     TRUE,                                       "save game"
15499   },
15500   {
15501     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15502     GAME_CTRL_ID_PAUSE2,                        NULL,
15503     TRUE,                                       "pause game"
15504   },
15505   {
15506     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15507     GAME_CTRL_ID_LOAD,                          NULL,
15508     TRUE,                                       "load game"
15509   },
15510   {
15511     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15512     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15513     FALSE,                                      "stop game"
15514   },
15515   {
15516     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15517     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15518     FALSE,                                      "pause game"
15519   },
15520   {
15521     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15522     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15523     FALSE,                                      "play game"
15524   },
15525   {
15526     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15527     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15528     TRUE,                                       "background music on/off"
15529   },
15530   {
15531     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15532     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15533     TRUE,                                       "sound loops on/off"
15534   },
15535   {
15536     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15537     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15538     TRUE,                                       "normal sounds on/off"
15539   },
15540   {
15541     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15542     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15543     FALSE,                                      "background music on/off"
15544   },
15545   {
15546     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15547     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15548     FALSE,                                      "sound loops on/off"
15549   },
15550   {
15551     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15552     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15553     FALSE,                                      "normal sounds on/off"
15554   }
15555 };
15556
15557 void CreateGameButtons(void)
15558 {
15559   int i;
15560
15561   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15562   {
15563     int graphic = gamebutton_info[i].graphic;
15564     struct GraphicInfo *gfx = &graphic_info[graphic];
15565     struct XY *pos = gamebutton_info[i].pos;
15566     struct GadgetInfo *gi;
15567     int button_type;
15568     boolean checked;
15569     unsigned int event_mask;
15570     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15571     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15572     int base_x = (on_tape ? VX : DX);
15573     int base_y = (on_tape ? VY : DY);
15574     int gd_x   = gfx->src_x;
15575     int gd_y   = gfx->src_y;
15576     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15577     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15578     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15579     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15580     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15581     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15582     int id = i;
15583
15584     if (gfx->bitmap == NULL)
15585     {
15586       game_gadget[id] = NULL;
15587
15588       continue;
15589     }
15590
15591     if (id == GAME_CTRL_ID_STOP ||
15592         id == GAME_CTRL_ID_PANEL_STOP ||
15593         id == GAME_CTRL_ID_PLAY ||
15594         id == GAME_CTRL_ID_PANEL_PLAY ||
15595         id == GAME_CTRL_ID_SAVE ||
15596         id == GAME_CTRL_ID_LOAD)
15597     {
15598       button_type = GD_TYPE_NORMAL_BUTTON;
15599       checked = FALSE;
15600       event_mask = GD_EVENT_RELEASED;
15601     }
15602     else if (id == GAME_CTRL_ID_UNDO ||
15603              id == GAME_CTRL_ID_REDO)
15604     {
15605       button_type = GD_TYPE_NORMAL_BUTTON;
15606       checked = FALSE;
15607       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15608     }
15609     else
15610     {
15611       button_type = GD_TYPE_CHECK_BUTTON;
15612       checked = (gamebutton_info[i].setup_value != NULL ?
15613                  *gamebutton_info[i].setup_value : FALSE);
15614       event_mask = GD_EVENT_PRESSED;
15615     }
15616
15617     gi = CreateGadget(GDI_CUSTOM_ID, id,
15618                       GDI_IMAGE_ID, graphic,
15619                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15620                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15621                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15622                       GDI_WIDTH, gfx->width,
15623                       GDI_HEIGHT, gfx->height,
15624                       GDI_TYPE, button_type,
15625                       GDI_STATE, GD_BUTTON_UNPRESSED,
15626                       GDI_CHECKED, checked,
15627                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15628                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15629                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15630                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15631                       GDI_DIRECT_DRAW, FALSE,
15632                       GDI_EVENT_MASK, event_mask,
15633                       GDI_CALLBACK_ACTION, HandleGameButtons,
15634                       GDI_END);
15635
15636     if (gi == NULL)
15637       Error(ERR_EXIT, "cannot create gadget");
15638
15639     game_gadget[id] = gi;
15640   }
15641 }
15642
15643 void FreeGameButtons(void)
15644 {
15645   int i;
15646
15647   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15648     FreeGadget(game_gadget[i]);
15649 }
15650
15651 static void UnmapGameButtonsAtSamePosition(int id)
15652 {
15653   int i;
15654
15655   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15656     if (i != id &&
15657         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15658         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15659       UnmapGadget(game_gadget[i]);
15660 }
15661
15662 static void UnmapGameButtonsAtSamePosition_All(void)
15663 {
15664   if (setup.show_snapshot_buttons)
15665   {
15666     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15667     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15668     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15669   }
15670   else
15671   {
15672     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15673     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15674     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15675
15676     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15677     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15678     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15679   }
15680 }
15681
15682 static void MapGameButtonsAtSamePosition(int id)
15683 {
15684   int i;
15685
15686   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15687     if (i != id &&
15688         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15689         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15690       MapGadget(game_gadget[i]);
15691
15692   UnmapGameButtonsAtSamePosition_All();
15693 }
15694
15695 void MapUndoRedoButtons(void)
15696 {
15697   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15698   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15699
15700   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15701   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15702
15703   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15704 }
15705
15706 void UnmapUndoRedoButtons(void)
15707 {
15708   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15709   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15710
15711   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15712   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15713
15714   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15715 }
15716
15717 static void MapGameButtonsExt(boolean on_tape)
15718 {
15719   int i;
15720
15721   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15722     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15723         i != GAME_CTRL_ID_UNDO &&
15724         i != GAME_CTRL_ID_REDO)
15725       MapGadget(game_gadget[i]);
15726
15727   UnmapGameButtonsAtSamePosition_All();
15728
15729   RedrawGameButtons();
15730 }
15731
15732 static void UnmapGameButtonsExt(boolean on_tape)
15733 {
15734   int i;
15735
15736   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15737     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15738       UnmapGadget(game_gadget[i]);
15739 }
15740
15741 static void RedrawGameButtonsExt(boolean on_tape)
15742 {
15743   int i;
15744
15745   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15746     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15747       RedrawGadget(game_gadget[i]);
15748
15749   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15750   redraw_mask &= ~REDRAW_ALL;
15751 }
15752
15753 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15754 {
15755   if (gi == NULL)
15756     return;
15757
15758   gi->checked = state;
15759 }
15760
15761 static void RedrawSoundButtonGadget(int id)
15762 {
15763   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15764              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15765              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15766              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15767              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15768              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15769              id);
15770
15771   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15772   RedrawGadget(game_gadget[id2]);
15773 }
15774
15775 void MapGameButtons(void)
15776 {
15777   MapGameButtonsExt(FALSE);
15778 }
15779
15780 void UnmapGameButtons(void)
15781 {
15782   UnmapGameButtonsExt(FALSE);
15783 }
15784
15785 void RedrawGameButtons(void)
15786 {
15787   RedrawGameButtonsExt(FALSE);
15788 }
15789
15790 void MapGameButtonsOnTape(void)
15791 {
15792   MapGameButtonsExt(TRUE);
15793 }
15794
15795 void UnmapGameButtonsOnTape(void)
15796 {
15797   UnmapGameButtonsExt(TRUE);
15798 }
15799
15800 void RedrawGameButtonsOnTape(void)
15801 {
15802   RedrawGameButtonsExt(TRUE);
15803 }
15804
15805 static void GameUndoRedoExt(void)
15806 {
15807   ClearPlayerAction();
15808
15809   tape.pausing = TRUE;
15810
15811   RedrawPlayfield();
15812   UpdateAndDisplayGameControlValues();
15813
15814   DrawCompleteVideoDisplay();
15815   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15816   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15817   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15818
15819   BackToFront();
15820 }
15821
15822 static void GameUndo(int steps)
15823 {
15824   if (!CheckEngineSnapshotList())
15825     return;
15826
15827   LoadEngineSnapshot_Undo(steps);
15828
15829   GameUndoRedoExt();
15830 }
15831
15832 static void GameRedo(int steps)
15833 {
15834   if (!CheckEngineSnapshotList())
15835     return;
15836
15837   LoadEngineSnapshot_Redo(steps);
15838
15839   GameUndoRedoExt();
15840 }
15841
15842 static void HandleGameButtonsExt(int id, int button)
15843 {
15844   static boolean game_undo_executed = FALSE;
15845   int steps = BUTTON_STEPSIZE(button);
15846   boolean handle_game_buttons =
15847     (game_status == GAME_MODE_PLAYING ||
15848      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15849
15850   if (!handle_game_buttons)
15851     return;
15852
15853   switch (id)
15854   {
15855     case GAME_CTRL_ID_STOP:
15856     case GAME_CTRL_ID_PANEL_STOP:
15857       if (game_status == GAME_MODE_MAIN)
15858         break;
15859
15860       if (tape.playing)
15861         TapeStop();
15862       else
15863         RequestQuitGame(TRUE);
15864
15865       break;
15866
15867     case GAME_CTRL_ID_PAUSE:
15868     case GAME_CTRL_ID_PAUSE2:
15869     case GAME_CTRL_ID_PANEL_PAUSE:
15870       if (network.enabled && game_status == GAME_MODE_PLAYING)
15871       {
15872         if (tape.pausing)
15873           SendToServer_ContinuePlaying();
15874         else
15875           SendToServer_PausePlaying();
15876       }
15877       else
15878         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15879
15880       game_undo_executed = FALSE;
15881
15882       break;
15883
15884     case GAME_CTRL_ID_PLAY:
15885     case GAME_CTRL_ID_PANEL_PLAY:
15886       if (game_status == GAME_MODE_MAIN)
15887       {
15888         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15889       }
15890       else if (tape.pausing)
15891       {
15892         if (network.enabled)
15893           SendToServer_ContinuePlaying();
15894         else
15895           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15896       }
15897       break;
15898
15899     case GAME_CTRL_ID_UNDO:
15900       // Important: When using "save snapshot when collecting an item" mode,
15901       // load last (current) snapshot for first "undo" after pressing "pause"
15902       // (else the last-but-one snapshot would be loaded, because the snapshot
15903       // pointer already points to the last snapshot when pressing "pause",
15904       // which is fine for "every step/move" mode, but not for "every collect")
15905       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15906           !game_undo_executed)
15907         steps--;
15908
15909       game_undo_executed = TRUE;
15910
15911       GameUndo(steps);
15912       break;
15913
15914     case GAME_CTRL_ID_REDO:
15915       GameRedo(steps);
15916       break;
15917
15918     case GAME_CTRL_ID_SAVE:
15919       TapeQuickSave();
15920       break;
15921
15922     case GAME_CTRL_ID_LOAD:
15923       TapeQuickLoad();
15924       break;
15925
15926     case SOUND_CTRL_ID_MUSIC:
15927     case SOUND_CTRL_ID_PANEL_MUSIC:
15928       if (setup.sound_music)
15929       { 
15930         setup.sound_music = FALSE;
15931
15932         FadeMusic();
15933       }
15934       else if (audio.music_available)
15935       { 
15936         setup.sound = setup.sound_music = TRUE;
15937
15938         SetAudioMode(setup.sound);
15939
15940         if (game_status == GAME_MODE_PLAYING)
15941           PlayLevelMusic();
15942       }
15943
15944       RedrawSoundButtonGadget(id);
15945
15946       break;
15947
15948     case SOUND_CTRL_ID_LOOPS:
15949     case SOUND_CTRL_ID_PANEL_LOOPS:
15950       if (setup.sound_loops)
15951         setup.sound_loops = FALSE;
15952       else if (audio.loops_available)
15953       {
15954         setup.sound = setup.sound_loops = TRUE;
15955
15956         SetAudioMode(setup.sound);
15957       }
15958
15959       RedrawSoundButtonGadget(id);
15960
15961       break;
15962
15963     case SOUND_CTRL_ID_SIMPLE:
15964     case SOUND_CTRL_ID_PANEL_SIMPLE:
15965       if (setup.sound_simple)
15966         setup.sound_simple = FALSE;
15967       else if (audio.sound_available)
15968       {
15969         setup.sound = setup.sound_simple = TRUE;
15970
15971         SetAudioMode(setup.sound);
15972       }
15973
15974       RedrawSoundButtonGadget(id);
15975
15976       break;
15977
15978     default:
15979       break;
15980   }
15981 }
15982
15983 static void HandleGameButtons(struct GadgetInfo *gi)
15984 {
15985   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15986 }
15987
15988 void HandleSoundButtonKeys(Key key)
15989 {
15990   if (key == setup.shortcut.sound_simple)
15991     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15992   else if (key == setup.shortcut.sound_loops)
15993     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15994   else if (key == setup.shortcut.sound_music)
15995     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15996 }