added using preferred network player also in single-player mode
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 // forward declaration for internal use
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 // for detection of endless loops, caused by custom element programming
1123 // (using maximal playfield width x 10 is just a rough approximation)
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 // ----------------------------------------------------------------------------
1153 // definition of elements that automatically change to other elements after
1154 // a specified time, eventually calling a function when changing
1155 // ----------------------------------------------------------------------------
1156
1157 // forward declaration for changer functions
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 // static variables for playfield scan mode (scanning forward or backward)
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   // make sure that stepsize value is always a power of 2
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   // do no immediately change move delay -- the player might just be moving
1623   player->move_delay_value_next = move_delay;
1624
1625   // information if player can move must be set separately
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void IncrementSokobanFieldsNeeded(void)
1687 {
1688   if (level.sb_fields_needed)
1689     game.sokoban_fields_still_needed++;
1690 }
1691
1692 static void IncrementSokobanObjectsNeeded(void)
1693 {
1694   if (level.sb_objects_needed)
1695     game.sokoban_objects_still_needed++;
1696 }
1697
1698 static void DecrementSokobanFieldsNeeded(void)
1699 {
1700   if (game.sokoban_fields_still_needed > 0)
1701     game.sokoban_fields_still_needed--;
1702 }
1703
1704 static void DecrementSokobanObjectsNeeded(void)
1705 {
1706   if (game.sokoban_objects_still_needed > 0)
1707     game.sokoban_objects_still_needed--;
1708 }
1709
1710 static void InitPlayerField(int x, int y, int element, boolean init_game)
1711 {
1712   if (element == EL_SP_MURPHY)
1713   {
1714     if (init_game)
1715     {
1716       if (stored_player[0].present)
1717       {
1718         Feld[x][y] = EL_SP_MURPHY_CLONE;
1719
1720         return;
1721       }
1722       else
1723       {
1724         stored_player[0].initial_element = element;
1725         stored_player[0].use_murphy = TRUE;
1726
1727         if (!level.use_artwork_element[0])
1728           stored_player[0].artwork_element = EL_SP_MURPHY;
1729       }
1730
1731       Feld[x][y] = EL_PLAYER_1;
1732     }
1733   }
1734
1735   if (init_game)
1736   {
1737     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1738     int jx = player->jx, jy = player->jy;
1739
1740     player->present = TRUE;
1741
1742     player->block_last_field = (element == EL_SP_MURPHY ?
1743                                 level.sp_block_last_field :
1744                                 level.block_last_field);
1745
1746     // ---------- initialize player's last field block delay ------------------
1747
1748     // always start with reliable default value (no adjustment needed)
1749     player->block_delay_adjustment = 0;
1750
1751     // special case 1: in Supaplex, Murphy blocks last field one more frame
1752     if (player->block_last_field && element == EL_SP_MURPHY)
1753       player->block_delay_adjustment = 1;
1754
1755     // special case 2: in game engines before 3.1.1, blocking was different
1756     if (game.use_block_last_field_bug)
1757       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1758
1759     if (!network.enabled || player->connected_network)
1760     {
1761       player->active = TRUE;
1762
1763       // remove potentially duplicate players
1764       if (StorePlayer[jx][jy] == Feld[x][y])
1765         StorePlayer[jx][jy] = 0;
1766
1767       StorePlayer[x][y] = Feld[x][y];
1768
1769 #if DEBUG_INIT_PLAYER
1770       if (options.debug)
1771       {
1772         printf("- player element %d activated", player->element_nr);
1773         printf(" (local player is %d and currently %s)\n",
1774                local_player->element_nr,
1775                local_player->active ? "active" : "not active");
1776       }
1777     }
1778 #endif
1779
1780     Feld[x][y] = EL_EMPTY;
1781
1782     player->jx = player->last_jx = x;
1783     player->jy = player->last_jy = y;
1784   }
1785
1786   if (!init_game)
1787   {
1788     int player_nr = GET_PLAYER_NR(element);
1789     struct PlayerInfo *player = &stored_player[player_nr];
1790
1791     if (player->active && player->killed)
1792       player->reanimated = TRUE; // if player was just killed, reanimate him
1793   }
1794 }
1795
1796 static void InitField(int x, int y, boolean init_game)
1797 {
1798   int element = Feld[x][y];
1799
1800   switch (element)
1801   {
1802     case EL_SP_MURPHY:
1803     case EL_PLAYER_1:
1804     case EL_PLAYER_2:
1805     case EL_PLAYER_3:
1806     case EL_PLAYER_4:
1807       InitPlayerField(x, y, element, init_game);
1808       break;
1809
1810     case EL_SOKOBAN_FIELD_PLAYER:
1811       element = Feld[x][y] = EL_PLAYER_1;
1812       InitField(x, y, init_game);
1813
1814       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1815       InitField(x, y, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_EMPTY:
1819       IncrementSokobanFieldsNeeded();
1820       break;
1821
1822     case EL_SOKOBAN_OBJECT:
1823       IncrementSokobanObjectsNeeded();
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       game.lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       game.friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    // more than one switch -- set it like the first switch
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950     case EL_LIGHT_SWITCH_ACTIVE:
1951       if (init_game)
1952         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1953       break;
1954
1955     case EL_INVISIBLE_STEELWALL:
1956     case EL_INVISIBLE_WALL:
1957     case EL_INVISIBLE_SAND:
1958       if (game.light_time_left > 0 ||
1959           game.lenses_time_left > 0)
1960         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1961       break;
1962
1963     case EL_EMC_MAGIC_BALL:
1964       if (game.ball_state)
1965         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1966       break;
1967
1968     case EL_EMC_MAGIC_BALL_SWITCH:
1969       if (game.ball_state)
1970         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1971       break;
1972
1973     case EL_TRIGGER_PLAYER:
1974     case EL_TRIGGER_ELEMENT:
1975     case EL_TRIGGER_CE_VALUE:
1976     case EL_TRIGGER_CE_SCORE:
1977     case EL_SELF:
1978     case EL_ANY_ELEMENT:
1979     case EL_CURRENT_CE_VALUE:
1980     case EL_CURRENT_CE_SCORE:
1981     case EL_PREV_CE_1:
1982     case EL_PREV_CE_2:
1983     case EL_PREV_CE_3:
1984     case EL_PREV_CE_4:
1985     case EL_PREV_CE_5:
1986     case EL_PREV_CE_6:
1987     case EL_PREV_CE_7:
1988     case EL_PREV_CE_8:
1989     case EL_NEXT_CE_1:
1990     case EL_NEXT_CE_2:
1991     case EL_NEXT_CE_3:
1992     case EL_NEXT_CE_4:
1993     case EL_NEXT_CE_5:
1994     case EL_NEXT_CE_6:
1995     case EL_NEXT_CE_7:
1996     case EL_NEXT_CE_8:
1997       // reference elements should not be used on the playfield
1998       Feld[x][y] = EL_EMPTY;
1999       break;
2000
2001     default:
2002       if (IS_CUSTOM_ELEMENT(element))
2003       {
2004         if (CAN_MOVE(element))
2005           InitMovDir(x, y);
2006
2007         if (!element_info[element].use_last_ce_value || init_game)
2008           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2009       }
2010       else if (IS_GROUP_ELEMENT(element))
2011       {
2012         Feld[x][y] = GetElementFromGroupElement(element);
2013
2014         InitField(x, y, init_game);
2015       }
2016
2017       break;
2018   }
2019
2020   if (!init_game)
2021     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2022 }
2023
2024 static void InitField_WithBug1(int x, int y, boolean init_game)
2025 {
2026   InitField(x, y, init_game);
2027
2028   // not needed to call InitMovDir() -- already done by InitField()!
2029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2030       CAN_MOVE(Feld[x][y]))
2031     InitMovDir(x, y);
2032 }
2033
2034 static void InitField_WithBug2(int x, int y, boolean init_game)
2035 {
2036   int old_element = Feld[x][y];
2037
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(old_element) &&
2043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2044     InitMovDir(x, y);
2045
2046   /* this case is in fact a combination of not less than three bugs:
2047      first, it calls InitMovDir() for elements that can move, although this is
2048      already done by InitField(); then, it checks the element that was at this
2049      field _before_ the call to InitField() (which can change it); lastly, it
2050      was not called for "mole with direction" elements, which were treated as
2051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2052   */
2053 }
2054
2055 static int get_key_element_from_nr(int key_nr)
2056 {
2057   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2058                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2059                           EL_EM_KEY_1 : EL_KEY_1);
2060
2061   return key_base_element + key_nr;
2062 }
2063
2064 static int get_next_dropped_element(struct PlayerInfo *player)
2065 {
2066   return (player->inventory_size > 0 ?
2067           player->inventory_element[player->inventory_size - 1] :
2068           player->inventory_infinite_element != EL_UNDEFINED ?
2069           player->inventory_infinite_element :
2070           player->dynabombs_left > 0 ?
2071           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2072           EL_UNDEFINED);
2073 }
2074
2075 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2076 {
2077   // pos >= 0: get element from bottom of the stack;
2078   // pos <  0: get element from top of the stack
2079
2080   if (pos < 0)
2081   {
2082     int min_inventory_size = -pos;
2083     int inventory_pos = player->inventory_size - min_inventory_size;
2084     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2085
2086     return (player->inventory_size >= min_inventory_size ?
2087             player->inventory_element[inventory_pos] :
2088             player->inventory_infinite_element != EL_UNDEFINED ?
2089             player->inventory_infinite_element :
2090             player->dynabombs_left >= min_dynabombs_left ?
2091             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2092             EL_UNDEFINED);
2093   }
2094   else
2095   {
2096     int min_dynabombs_left = pos + 1;
2097     int min_inventory_size = pos + 1 - player->dynabombs_left;
2098     int inventory_pos = pos - player->dynabombs_left;
2099
2100     return (player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             player->inventory_size >= min_inventory_size ?
2105             player->inventory_element[inventory_pos] :
2106             EL_UNDEFINED);
2107   }
2108 }
2109
2110 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2111 {
2112   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2113   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2114   int compare_result;
2115
2116   if (gpo1->sort_priority != gpo2->sort_priority)
2117     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2118   else
2119     compare_result = gpo1->nr - gpo2->nr;
2120
2121   return compare_result;
2122 }
2123
2124 int getPlayerInventorySize(int player_nr)
2125 {
2126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2127     return level.native_em_level->ply[player_nr]->dynamite;
2128   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2129     return level.native_sp_level->game_sp->red_disk_count;
2130   else
2131     return stored_player[player_nr].inventory_size;
2132 }
2133
2134 static void InitGameControlValues(void)
2135 {
2136   int i;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2142     struct TextPosInfo *pos = gpc->pos;
2143     int nr = gpc->nr;
2144     int type = gpc->type;
2145
2146     if (nr != i)
2147     {
2148       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2149       Error(ERR_EXIT, "this should not happen -- please debug");
2150     }
2151
2152     // force update of game controls after initialization
2153     gpc->value = gpc->last_value = -1;
2154     gpc->frame = gpc->last_frame = -1;
2155     gpc->gfx_frame = -1;
2156
2157     // determine panel value width for later calculation of alignment
2158     if (type == TYPE_INTEGER || type == TYPE_STRING)
2159     {
2160       pos->width = pos->size * getFontWidth(pos->font);
2161       pos->height = getFontHeight(pos->font);
2162     }
2163     else if (type == TYPE_ELEMENT)
2164     {
2165       pos->width = pos->size;
2166       pos->height = pos->size;
2167     }
2168
2169     // fill structure for game panel draw order
2170     gpo->nr = gpc->nr;
2171     gpo->sort_priority = pos->sort_priority;
2172   }
2173
2174   // sort game panel controls according to sort_priority and control number
2175   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2176         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2177 }
2178
2179 static void UpdatePlayfieldElementCount(void)
2180 {
2181   boolean use_element_count = FALSE;
2182   int i, j, x, y;
2183
2184   // first check if it is needed at all to calculate playfield element count
2185   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2186     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2187       use_element_count = TRUE;
2188
2189   if (!use_element_count)
2190     return;
2191
2192   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2193     element_info[i].element_count = 0;
2194
2195   SCAN_PLAYFIELD(x, y)
2196   {
2197     element_info[Feld[x][y]].element_count++;
2198   }
2199
2200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2201     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2202       if (IS_IN_GROUP(j, i))
2203         element_info[EL_GROUP_START + i].element_count +=
2204           element_info[j].element_count;
2205 }
2206
2207 static void UpdateGameControlValues(void)
2208 {
2209   int i, k;
2210   int time = (game.LevelSolved ?
2211               game.LevelSolved_CountingTime :
2212               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2213               level.native_em_level->lev->time :
2214               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2215               level.native_sp_level->game_sp->time_played :
2216               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2217               game_mm.energy_left :
2218               game.no_time_limit ? TimePlayed : TimeLeft);
2219   int score = (game.LevelSolved ?
2220                game.LevelSolved_CountingScore :
2221                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222                level.native_em_level->lev->score :
2223                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224                level.native_sp_level->game_sp->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226                game_mm.score :
2227                game.score);
2228   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229               level.native_em_level->lev->required :
2230               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231               level.native_sp_level->game_sp->infotrons_still_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2233               game_mm.kettles_still_needed :
2234               game.gems_still_needed);
2235   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236                      level.native_em_level->lev->required > 0 :
2237                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2240                      game_mm.kettles_still_needed > 0 ||
2241                      game_mm.lights_still_needed > 0 :
2242                      game.gems_still_needed > 0 ||
2243                      game.sokoban_fields_still_needed > 0 ||
2244                      game.sokoban_objects_still_needed > 0 ||
2245                      game.lights_still_needed > 0);
2246   int health = (game.LevelSolved ?
2247                 game.LevelSolved_CountingHealth :
2248                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                 MM_HEALTH(game_mm.laser_overload_value) :
2250                 game.health);
2251
2252   UpdatePlayfieldElementCount();
2253
2254   // update game panel control values
2255
2256   // used instead of "level_nr" (for network games)
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       // only one player in Supaplex game engine
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272         break;
2273
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277         {
2278           if (level.native_em_level->ply[i]->keys & (1 << k))
2279             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280               get_key_element_from_nr(k);
2281         }
2282         else if (stored_player[i].key[k])
2283           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284             get_key_element_from_nr(k);
2285       }
2286
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         getPlayerInventorySize(i);
2289
2290       if (stored_player[i].num_white_keys > 0)
2291         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2292           EL_DC_KEY_WHITE;
2293
2294       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2295         stored_player[i].num_white_keys;
2296     }
2297   }
2298   else
2299   {
2300     int player_nr = game.centered_player_nr;
2301
2302     for (k = 0; k < MAX_NUM_KEYS; k++)
2303     {
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2305       {
2306         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2307           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2308             get_key_element_from_nr(k);
2309       }
2310       else if (stored_player[player_nr].key[k])
2311         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2312           get_key_element_from_nr(k);
2313     }
2314
2315     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2316       getPlayerInventorySize(player_nr);
2317
2318     if (stored_player[player_nr].num_white_keys > 0)
2319       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2320
2321     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2322       stored_player[player_nr].num_white_keys;
2323   }
2324
2325   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2326   {
2327     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2328       get_inventory_element_from_pos(local_player, i);
2329     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, -i - 1);
2331   }
2332
2333   game_panel_controls[GAME_PANEL_SCORE].value = score;
2334   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2335
2336   game_panel_controls[GAME_PANEL_TIME].value = time;
2337
2338   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2339   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2340   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2341
2342   if (level.time == 0)
2343     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2344   else
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2346
2347   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2348   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2349
2350   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2351
2352   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2353     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2354      EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2356     local_player->shield_normal_time_left;
2357   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2358     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2359      EL_EMPTY);
2360   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2361     local_player->shield_deadly_time_left;
2362
2363   game_panel_controls[GAME_PANEL_EXIT].value =
2364     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2367     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2370      EL_EMC_MAGIC_BALL_SWITCH);
2371
2372   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2373     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2375     game.light_time_left;
2376
2377   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2378     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2380     game.timegate_time_left;
2381
2382   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2383     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2384
2385   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2386     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2387   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2388     game.lenses_time_left;
2389
2390   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2391     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2393     game.magnify_time_left;
2394
2395   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2396     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2397      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2398      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2399      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2400      EL_BALLOON_SWITCH_NONE);
2401
2402   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2403     local_player->dynabomb_count;
2404   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2405     local_player->dynabomb_size;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2407     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2408
2409   game_panel_controls[GAME_PANEL_PENGUINS].value =
2410     game.friends_still_needed;
2411
2412   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2413     game.sokoban_objects_still_needed;
2414   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2415     game.sokoban_fields_still_needed;
2416
2417   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2418     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2419
2420   for (i = 0; i < NUM_BELTS; i++)
2421   {
2422     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2423       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2424        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2425     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2426       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427   }
2428
2429   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2430     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2432     game.magic_wall_time_left;
2433
2434   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2435     local_player->gravity;
2436
2437   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2438     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2439
2440   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2441     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2442       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2443        game.panel.element[i].id : EL_UNDEFINED);
2444
2445   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2446     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2447       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2448        element_info[game.panel.element_count[i].id].element_count : 0);
2449
2450   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2451     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2452       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2453        element_info[game.panel.ce_score[i].id].collect_score : 0);
2454
2455   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2456     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2457       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2458        element_info[game.panel.ce_score_element[i].id].collect_score :
2459        EL_UNDEFINED);
2460
2461   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2462   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2463   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2464
2465   // update game panel control frames
2466
2467   for (i = 0; game_panel_controls[i].nr != -1; i++)
2468   {
2469     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2470
2471     if (gpc->type == TYPE_ELEMENT)
2472     {
2473       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2474       {
2475         int last_anim_random_frame = gfx.anim_random_frame;
2476         int element = gpc->value;
2477         int graphic = el2panelimg(element);
2478
2479         if (gpc->value != gpc->last_value)
2480         {
2481           gpc->gfx_frame = 0;
2482           gpc->gfx_random = INIT_GFX_RANDOM();
2483         }
2484         else
2485         {
2486           gpc->gfx_frame++;
2487
2488           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2489               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2490             gpc->gfx_random = INIT_GFX_RANDOM();
2491         }
2492
2493         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2494           gfx.anim_random_frame = gpc->gfx_random;
2495
2496         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2497           gpc->gfx_frame = element_info[element].collect_score;
2498
2499         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2500                                               gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506     else if (gpc->type == TYPE_GRAPHIC)
2507     {
2508       if (gpc->graphic != IMG_UNDEFINED)
2509       {
2510         int last_anim_random_frame = gfx.anim_random_frame;
2511         int graphic = gpc->graphic;
2512
2513         if (gpc->value != gpc->last_value)
2514         {
2515           gpc->gfx_frame = 0;
2516           gpc->gfx_random = INIT_GFX_RANDOM();
2517         }
2518         else
2519         {
2520           gpc->gfx_frame++;
2521
2522           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2523               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2524             gpc->gfx_random = INIT_GFX_RANDOM();
2525         }
2526
2527         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2528           gfx.anim_random_frame = gpc->gfx_random;
2529
2530         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2531
2532         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2533           gfx.anim_random_frame = last_anim_random_frame;
2534       }
2535     }
2536   }
2537 }
2538
2539 static void DisplayGameControlValues(void)
2540 {
2541   boolean redraw_panel = FALSE;
2542   int i;
2543
2544   for (i = 0; game_panel_controls[i].nr != -1; i++)
2545   {
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2547
2548     if (PANEL_DEACTIVATED(gpc->pos))
2549       continue;
2550
2551     if (gpc->value == gpc->last_value &&
2552         gpc->frame == gpc->last_frame)
2553       continue;
2554
2555     redraw_panel = TRUE;
2556   }
2557
2558   if (!redraw_panel)
2559     return;
2560
2561   // copy default game door content to main double buffer
2562
2563   // !!! CHECK AGAIN !!!
2564   SetPanelBackground();
2565   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2566   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2567
2568   // redraw game control buttons
2569   RedrawGameButtons();
2570
2571   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2572
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574   {
2575     int nr = game_panel_order[i].nr;
2576     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2577     struct TextPosInfo *pos = gpc->pos;
2578     int type = gpc->type;
2579     int value = gpc->value;
2580     int frame = gpc->frame;
2581     int size = pos->size;
2582     int font = pos->font;
2583     boolean draw_masked = pos->draw_masked;
2584     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2585
2586     if (PANEL_DEACTIVATED(pos))
2587       continue;
2588
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591
2592     if (type == TYPE_INTEGER)
2593     {
2594       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2595           nr == GAME_PANEL_TIME)
2596       {
2597         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2598
2599         if (use_dynamic_size)           // use dynamic number of digits
2600         {
2601           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2602           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2603           int size2 = size1 + 1;
2604           int font1 = pos->font;
2605           int font2 = pos->font_alt;
2606
2607           size = (value < value_change ? size1 : size2);
2608           font = (value < value_change ? font1 : font2);
2609         }
2610       }
2611
2612       // correct text size if "digits" is zero or less
2613       if (size <= 0)
2614         size = strlen(int2str(value, size));
2615
2616       // dynamically correct text alignment
2617       pos->width = size * getFontWidth(font);
2618
2619       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2620                   int2str(value, size), font, mask_mode);
2621     }
2622     else if (type == TYPE_ELEMENT)
2623     {
2624       int element, graphic;
2625       Bitmap *src_bitmap;
2626       int src_x, src_y;
2627       int width, height;
2628       int dst_x = PANEL_XPOS(pos);
2629       int dst_y = PANEL_YPOS(pos);
2630
2631       if (value != EL_UNDEFINED && value != EL_EMPTY)
2632       {
2633         element = value;
2634         graphic = el2panelimg(value);
2635
2636         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2637
2638         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2639           size = TILESIZE;
2640
2641         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2642                               &src_x, &src_y);
2643
2644         width  = graphic_info[graphic].width  * size / TILESIZE;
2645         height = graphic_info[graphic].height * size / TILESIZE;
2646
2647         if (draw_masked)
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         else
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653       }
2654     }
2655     else if (type == TYPE_GRAPHIC)
2656     {
2657       int graphic        = gpc->graphic;
2658       int graphic_active = gpc->graphic_active;
2659       Bitmap *src_bitmap;
2660       int src_x, src_y;
2661       int width, height;
2662       int dst_x = PANEL_XPOS(pos);
2663       int dst_y = PANEL_YPOS(pos);
2664       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2665                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2666
2667       if (graphic != IMG_UNDEFINED && !skip)
2668       {
2669         if (pos->style == STYLE_REVERSE)
2670           value = 100 - value;
2671
2672         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2673
2674         if (pos->direction & MV_HORIZONTAL)
2675         {
2676           width  = graphic_info[graphic_active].width * value / 100;
2677           height = graphic_info[graphic_active].height;
2678
2679           if (pos->direction == MV_LEFT)
2680           {
2681             src_x += graphic_info[graphic_active].width - width;
2682             dst_x += graphic_info[graphic_active].width - width;
2683           }
2684         }
2685         else
2686         {
2687           width  = graphic_info[graphic_active].width;
2688           height = graphic_info[graphic_active].height * value / 100;
2689
2690           if (pos->direction == MV_UP)
2691           {
2692             src_y += graphic_info[graphic_active].height - height;
2693             dst_y += graphic_info[graphic_active].height - height;
2694           }
2695         }
2696
2697         if (draw_masked)
2698           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2699                            dst_x, dst_y);
2700         else
2701           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2702                      dst_x, dst_y);
2703
2704         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2705
2706         if (pos->direction & MV_HORIZONTAL)
2707         {
2708           if (pos->direction == MV_RIGHT)
2709           {
2710             src_x += width;
2711             dst_x += width;
2712           }
2713           else
2714           {
2715             dst_x = PANEL_XPOS(pos);
2716           }
2717
2718           width = graphic_info[graphic].width - width;
2719         }
2720         else
2721         {
2722           if (pos->direction == MV_DOWN)
2723           {
2724             src_y += height;
2725             dst_y += height;
2726           }
2727           else
2728           {
2729             dst_y = PANEL_YPOS(pos);
2730           }
2731
2732           height = graphic_info[graphic].height - height;
2733         }
2734
2735         if (draw_masked)
2736           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2737                            dst_x, dst_y);
2738         else
2739           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2740                      dst_x, dst_y);
2741       }
2742     }
2743     else if (type == TYPE_STRING)
2744     {
2745       boolean active = (value != 0);
2746       char *state_normal = "off";
2747       char *state_active = "on";
2748       char *state = (active ? state_active : state_normal);
2749       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2750                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2751                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2752                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2753
2754       if (nr == GAME_PANEL_GRAVITY_STATE)
2755       {
2756         int font1 = pos->font;          // (used for normal state)
2757         int font2 = pos->font_alt;      // (used for active state)
2758
2759         font = (active ? font2 : font1);
2760       }
2761
2762       if (s != NULL)
2763       {
2764         char *s_cut;
2765
2766         if (size <= 0)
2767         {
2768           // don't truncate output if "chars" is zero or less
2769           size = strlen(s);
2770
2771           // dynamically correct text alignment
2772           pos->width = size * getFontWidth(font);
2773         }
2774
2775         s_cut = getStringCopyN(s, size);
2776
2777         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2778                     s_cut, font, mask_mode);
2779
2780         free(s_cut);
2781       }
2782     }
2783
2784     redraw_mask |= REDRAW_DOOR_1;
2785   }
2786
2787   SetGameStatus(GAME_MODE_PLAYING);
2788 }
2789
2790 void UpdateAndDisplayGameControlValues(void)
2791 {
2792   if (tape.deactivate_display)
2793     return;
2794
2795   UpdateGameControlValues();
2796   DisplayGameControlValues();
2797 }
2798
2799 #if 0
2800 static void UpdateGameDoorValues(void)
2801 {
2802   UpdateGameControlValues();
2803 }
2804 #endif
2805
2806 void DrawGameDoorValues(void)
2807 {
2808   DisplayGameControlValues();
2809 }
2810
2811
2812 // ============================================================================
2813 // InitGameEngine()
2814 // ----------------------------------------------------------------------------
2815 // initialize game engine due to level / tape version number
2816 // ============================================================================
2817
2818 static void InitGameEngine(void)
2819 {
2820   int i, j, k, l, x, y;
2821
2822   // set game engine from tape file when re-playing, else from level file
2823   game.engine_version = (tape.playing ? tape.engine_version :
2824                          level.game_version);
2825
2826   // set single or multi-player game mode (needed for re-playing tapes)
2827   game.team_mode = setup.team_mode;
2828
2829   if (tape.playing)
2830   {
2831     int num_players = 0;
2832
2833     for (i = 0; i < MAX_PLAYERS; i++)
2834       if (tape.player_participates[i])
2835         num_players++;
2836
2837     // multi-player tapes contain input data for more than one player
2838     game.team_mode = (num_players > 1);
2839   }
2840
2841   // --------------------------------------------------------------------------
2842   // set flags for bugs and changes according to active game engine version
2843   // --------------------------------------------------------------------------
2844
2845   /*
2846     Summary of bugfix/change:
2847     Fixed handling for custom elements that change when pushed by the player.
2848
2849     Fixed/changed in version:
2850     3.1.0
2851
2852     Description:
2853     Before 3.1.0, custom elements that "change when pushing" changed directly
2854     after the player started pushing them (until then handled in "DigField()").
2855     Since 3.1.0, these custom elements are not changed until the "pushing"
2856     move of the element is finished (now handled in "ContinueMoving()").
2857
2858     Affected levels/tapes:
2859     The first condition is generally needed for all levels/tapes before version
2860     3.1.0, which might use the old behaviour before it was changed; known tapes
2861     that are affected are some tapes from the level set "Walpurgis Gardens" by
2862     Jamie Cullen.
2863     The second condition is an exception from the above case and is needed for
2864     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2865     above (including some development versions of 3.1.0), but before it was
2866     known that this change would break tapes like the above and was fixed in
2867     3.1.1, so that the changed behaviour was active although the engine version
2868     while recording maybe was before 3.1.0. There is at least one tape that is
2869     affected by this exception, which is the tape for the one-level set "Bug
2870     Machine" by Juergen Bonhagen.
2871   */
2872
2873   game.use_change_when_pushing_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2875      !(tape.playing &&
2876        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2877        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2878
2879   /*
2880     Summary of bugfix/change:
2881     Fixed handling for blocking the field the player leaves when moving.
2882
2883     Fixed/changed in version:
2884     3.1.1
2885
2886     Description:
2887     Before 3.1.1, when "block last field when moving" was enabled, the field
2888     the player is leaving when moving was blocked for the time of the move,
2889     and was directly unblocked afterwards. This resulted in the last field
2890     being blocked for exactly one less than the number of frames of one player
2891     move. Additionally, even when blocking was disabled, the last field was
2892     blocked for exactly one frame.
2893     Since 3.1.1, due to changes in player movement handling, the last field
2894     is not blocked at all when blocking is disabled. When blocking is enabled,
2895     the last field is blocked for exactly the number of frames of one player
2896     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2897     last field is blocked for exactly one more than the number of frames of
2898     one player move.
2899
2900     Affected levels/tapes:
2901     (!!! yet to be determined -- probably many !!!)
2902   */
2903
2904   game.use_block_last_field_bug =
2905     (game.engine_version < VERSION_IDENT(3,1,1,0));
2906
2907   game_em.use_single_button =
2908     (game.engine_version > VERSION_IDENT(4,0,0,2));
2909
2910   game_em.use_snap_key_bug =
2911     (game.engine_version < VERSION_IDENT(4,0,1,0));
2912
2913   // --------------------------------------------------------------------------
2914
2915   // set maximal allowed number of custom element changes per game frame
2916   game.max_num_changes_per_frame = 1;
2917
2918   // default scan direction: scan playfield from top/left to bottom/right
2919   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2920
2921   // dynamically adjust element properties according to game engine version
2922   InitElementPropertiesEngine(game.engine_version);
2923
2924 #if 0
2925   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2926   printf("          tape version == %06d [%s] [file: %06d]\n",
2927          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2928          tape.file_version);
2929   printf("       => game.engine_version == %06d\n", game.engine_version);
2930 #endif
2931
2932   // ---------- initialize player's initial move delay ------------------------
2933
2934   // dynamically adjust player properties according to level information
2935   for (i = 0; i < MAX_PLAYERS; i++)
2936     game.initial_move_delay_value[i] =
2937       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2938
2939   // dynamically adjust player properties according to game engine version
2940   for (i = 0; i < MAX_PLAYERS; i++)
2941     game.initial_move_delay[i] =
2942       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2943        game.initial_move_delay_value[i] : 0);
2944
2945   // ---------- initialize player's initial push delay ------------------------
2946
2947   // dynamically adjust player properties according to game engine version
2948   game.initial_push_delay_value =
2949     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2950
2951   // ---------- initialize changing elements ----------------------------------
2952
2953   // initialize changing elements information
2954   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2955   {
2956     struct ElementInfo *ei = &element_info[i];
2957
2958     // this pointer might have been changed in the level editor
2959     ei->change = &ei->change_page[0];
2960
2961     if (!IS_CUSTOM_ELEMENT(i))
2962     {
2963       ei->change->target_element = EL_EMPTY_SPACE;
2964       ei->change->delay_fixed = 0;
2965       ei->change->delay_random = 0;
2966       ei->change->delay_frames = 1;
2967     }
2968
2969     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2970     {
2971       ei->has_change_event[j] = FALSE;
2972
2973       ei->event_page_nr[j] = 0;
2974       ei->event_page[j] = &ei->change_page[0];
2975     }
2976   }
2977
2978   // add changing elements from pre-defined list
2979   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2980   {
2981     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2982     struct ElementInfo *ei = &element_info[ch_delay->element];
2983
2984     ei->change->target_element       = ch_delay->target_element;
2985     ei->change->delay_fixed          = ch_delay->change_delay;
2986
2987     ei->change->pre_change_function  = ch_delay->pre_change_function;
2988     ei->change->change_function      = ch_delay->change_function;
2989     ei->change->post_change_function = ch_delay->post_change_function;
2990
2991     ei->change->can_change = TRUE;
2992     ei->change->can_change_or_has_action = TRUE;
2993
2994     ei->has_change_event[CE_DELAY] = TRUE;
2995
2996     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2997     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2998   }
2999
3000   // ---------- initialize internal run-time variables ------------------------
3001
3002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3003   {
3004     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3005
3006     for (j = 0; j < ei->num_change_pages; j++)
3007     {
3008       ei->change_page[j].can_change_or_has_action =
3009         (ei->change_page[j].can_change |
3010          ei->change_page[j].has_action);
3011     }
3012   }
3013
3014   // add change events from custom element configuration
3015   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3016   {
3017     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3018
3019     for (j = 0; j < ei->num_change_pages; j++)
3020     {
3021       if (!ei->change_page[j].can_change_or_has_action)
3022         continue;
3023
3024       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3025       {
3026         // only add event page for the first page found with this event
3027         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3028         {
3029           ei->has_change_event[k] = TRUE;
3030
3031           ei->event_page_nr[k] = j;
3032           ei->event_page[k] = &ei->change_page[j];
3033         }
3034       }
3035     }
3036   }
3037
3038   // ---------- initialize reference elements in change conditions ------------
3039
3040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3041   {
3042     int element = EL_CUSTOM_START + i;
3043     struct ElementInfo *ei = &element_info[element];
3044
3045     for (j = 0; j < ei->num_change_pages; j++)
3046     {
3047       int trigger_element = ei->change_page[j].initial_trigger_element;
3048
3049       if (trigger_element >= EL_PREV_CE_8 &&
3050           trigger_element <= EL_NEXT_CE_8)
3051         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3052
3053       ei->change_page[j].trigger_element = trigger_element;
3054     }
3055   }
3056
3057   // ---------- initialize run-time trigger player and element ----------------
3058
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3066       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3067       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3068       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3069       ei->change_page[j].actual_trigger_ce_value = 0;
3070       ei->change_page[j].actual_trigger_ce_score = 0;
3071     }
3072   }
3073
3074   // ---------- initialize trigger events -------------------------------------
3075
3076   // initialize trigger events information
3077   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3078     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3079       trigger_events[i][j] = FALSE;
3080
3081   // add trigger events from element change event properties
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083   {
3084     struct ElementInfo *ei = &element_info[i];
3085
3086     for (j = 0; j < ei->num_change_pages; j++)
3087     {
3088       if (!ei->change_page[j].can_change_or_has_action)
3089         continue;
3090
3091       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3092       {
3093         int trigger_element = ei->change_page[j].trigger_element;
3094
3095         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3096         {
3097           if (ei->change_page[j].has_event[k])
3098           {
3099             if (IS_GROUP_ELEMENT(trigger_element))
3100             {
3101               struct ElementGroupInfo *group =
3102                 element_info[trigger_element].group;
3103
3104               for (l = 0; l < group->num_elements_resolved; l++)
3105                 trigger_events[group->element_resolved[l]][k] = TRUE;
3106             }
3107             else if (trigger_element == EL_ANY_ELEMENT)
3108               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3109                 trigger_events[l][k] = TRUE;
3110             else
3111               trigger_events[trigger_element][k] = TRUE;
3112           }
3113         }
3114       }
3115     }
3116   }
3117
3118   // ---------- initialize push delay -----------------------------------------
3119
3120   // initialize push delay values to default
3121   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122   {
3123     if (!IS_CUSTOM_ELEMENT(i))
3124     {
3125       // set default push delay values (corrected since version 3.0.7-1)
3126       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3127       {
3128         element_info[i].push_delay_fixed = 2;
3129         element_info[i].push_delay_random = 8;
3130       }
3131       else
3132       {
3133         element_info[i].push_delay_fixed = 8;
3134         element_info[i].push_delay_random = 8;
3135       }
3136     }
3137   }
3138
3139   // set push delay value for certain elements from pre-defined list
3140   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     int e = push_delay_list[i].element;
3143
3144     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3145     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3146   }
3147
3148   // set push delay value for Supaplex elements for newer engine versions
3149   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3150   {
3151     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     {
3153       if (IS_SP_ELEMENT(i))
3154       {
3155         // set SP push delay to just enough to push under a falling zonk
3156         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3157
3158         element_info[i].push_delay_fixed  = delay;
3159         element_info[i].push_delay_random = 0;
3160       }
3161     }
3162   }
3163
3164   // ---------- initialize move stepsize --------------------------------------
3165
3166   // initialize move stepsize values to default
3167   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3168     if (!IS_CUSTOM_ELEMENT(i))
3169       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3170
3171   // set move stepsize value for certain elements from pre-defined list
3172   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3173   {
3174     int e = move_stepsize_list[i].element;
3175
3176     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3177   }
3178
3179   // ---------- initialize collect score --------------------------------------
3180
3181   // initialize collect score values for custom elements from initial value
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183     if (IS_CUSTOM_ELEMENT(i))
3184       element_info[i].collect_score = element_info[i].collect_score_initial;
3185
3186   // ---------- initialize collect count --------------------------------------
3187
3188   // initialize collect count values for non-custom elements
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (!IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_count_initial = 0;
3192
3193   // add collect count values for all elements from pre-defined list
3194   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3195     element_info[collect_count_list[i].element].collect_count_initial =
3196       collect_count_list[i].count;
3197
3198   // ---------- initialize access direction -----------------------------------
3199
3200   // initialize access direction values to default (access from every side)
3201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3202     if (!IS_CUSTOM_ELEMENT(i))
3203       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3204
3205   // set access direction value for certain elements from pre-defined list
3206   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3207     element_info[access_direction_list[i].element].access_direction =
3208       access_direction_list[i].direction;
3209
3210   // ---------- initialize explosion content ----------------------------------
3211   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3212   {
3213     if (IS_CUSTOM_ELEMENT(i))
3214       continue;
3215
3216     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3217     {
3218       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3219
3220       element_info[i].content.e[x][y] =
3221         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3222          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3223          i == EL_PLAYER_3 ? EL_EMERALD :
3224          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3225          i == EL_MOLE ? EL_EMERALD_RED :
3226          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3227          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3228          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3229          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3230          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3231          i == EL_WALL_EMERALD ? EL_EMERALD :
3232          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3233          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3234          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3235          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3236          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3237          i == EL_WALL_PEARL ? EL_PEARL :
3238          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3239          EL_EMPTY);
3240     }
3241   }
3242
3243   // ---------- initialize recursion detection --------------------------------
3244   recursion_loop_depth = 0;
3245   recursion_loop_detected = FALSE;
3246   recursion_loop_element = EL_UNDEFINED;
3247
3248   // ---------- initialize graphics engine ------------------------------------
3249   game.scroll_delay_value =
3250     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3251      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3252   game.scroll_delay_value =
3253     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3254
3255   // ---------- initialize game engine snapshots ------------------------------
3256   for (i = 0; i < MAX_PLAYERS; i++)
3257     game.snapshot.last_action[i] = 0;
3258   game.snapshot.changed_action = FALSE;
3259   game.snapshot.collected_item = FALSE;
3260   game.snapshot.mode =
3261     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3262      SNAPSHOT_MODE_EVERY_STEP :
3263      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3264      SNAPSHOT_MODE_EVERY_MOVE :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3266      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3267   game.snapshot.save_snapshot = FALSE;
3268
3269   // ---------- initialize level time for Supaplex engine ---------------------
3270   // Supaplex levels with time limit currently unsupported -- should be added
3271   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3272     level.time = 0;
3273 }
3274
3275 static int get_num_special_action(int element, int action_first,
3276                                   int action_last)
3277 {
3278   int num_special_action = 0;
3279   int i, j;
3280
3281   for (i = action_first; i <= action_last; i++)
3282   {
3283     boolean found = FALSE;
3284
3285     for (j = 0; j < NUM_DIRECTIONS; j++)
3286       if (el_act_dir2img(element, i, j) !=
3287           el_act_dir2img(element, ACTION_DEFAULT, j))
3288         found = TRUE;
3289
3290     if (found)
3291       num_special_action++;
3292     else
3293       break;
3294   }
3295
3296   return num_special_action;
3297 }
3298
3299
3300 // ============================================================================
3301 // InitGame()
3302 // ----------------------------------------------------------------------------
3303 // initialize and start new game
3304 // ============================================================================
3305
3306 #if DEBUG_INIT_PLAYER
3307 static void DebugPrintPlayerStatus(char *message)
3308 {
3309   int i;
3310
3311   if (!options.debug)
3312     return;
3313
3314   printf("%s:\n", message);
3315
3316   for (i = 0; i < MAX_PLAYERS; i++)
3317   {
3318     struct PlayerInfo *player = &stored_player[i];
3319
3320     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3321            i + 1,
3322            player->present,
3323            player->connected,
3324            player->connected_locally,
3325            player->connected_network,
3326            player->active);
3327
3328     if (local_player == player)
3329       printf(" (local player)");
3330
3331     printf("\n");
3332   }
3333 }
3334 #endif
3335
3336 void InitGame(void)
3337 {
3338   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3339   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3340   int fade_mask = REDRAW_FIELD;
3341
3342   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3343   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3344   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3345   int initial_move_dir = MV_DOWN;
3346   int i, j, x, y;
3347
3348   // required here to update video display before fading (FIX THIS)
3349   DrawMaskedBorder(REDRAW_DOOR_2);
3350
3351   if (!game.restart_level)
3352     CloseDoor(DOOR_CLOSE_1);
3353
3354   SetGameStatus(GAME_MODE_PLAYING);
3355
3356   if (level_editor_test_game)
3357     FadeSkipNextFadeIn();
3358   else
3359     FadeSetEnterScreen();
3360
3361   if (CheckFadeAll())
3362     fade_mask = REDRAW_ALL;
3363
3364   FadeLevelSoundsAndMusic();
3365
3366   ExpireSoundLoops(TRUE);
3367
3368   if (!level_editor_test_game)
3369     FadeOut(fade_mask);
3370
3371   // needed if different viewport properties defined for playing
3372   ChangeViewportPropertiesIfNeeded();
3373
3374   ClearField();
3375
3376   DrawCompleteVideoDisplay();
3377
3378   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3379
3380   InitGameEngine();
3381   InitGameControlValues();
3382
3383   // don't play tapes over network
3384   network_playing = (network.enabled && !tape.playing);
3385
3386   for (i = 0; i < MAX_PLAYERS; i++)
3387   {
3388     struct PlayerInfo *player = &stored_player[i];
3389
3390     player->index_nr = i;
3391     player->index_bit = (1 << i);
3392     player->element_nr = EL_PLAYER_1 + i;
3393
3394     player->present = FALSE;
3395     player->active = FALSE;
3396     player->mapped = FALSE;
3397
3398     player->killed = FALSE;
3399     player->reanimated = FALSE;
3400     player->buried = FALSE;
3401
3402     player->action = 0;
3403     player->effective_action = 0;
3404     player->programmed_action = 0;
3405
3406     player->mouse_action.lx = 0;
3407     player->mouse_action.ly = 0;
3408     player->mouse_action.button = 0;
3409     player->mouse_action.button_hint = 0;
3410
3411     player->effective_mouse_action.lx = 0;
3412     player->effective_mouse_action.ly = 0;
3413     player->effective_mouse_action.button = 0;
3414     player->effective_mouse_action.button_hint = 0;
3415
3416     for (j = 0; j < MAX_NUM_KEYS; j++)
3417       player->key[j] = FALSE;
3418
3419     player->num_white_keys = 0;
3420
3421     player->dynabomb_count = 0;
3422     player->dynabomb_size = 1;
3423     player->dynabombs_left = 0;
3424     player->dynabomb_xl = FALSE;
3425
3426     player->MovDir = initial_move_dir;
3427     player->MovPos = 0;
3428     player->GfxPos = 0;
3429     player->GfxDir = initial_move_dir;
3430     player->GfxAction = ACTION_DEFAULT;
3431     player->Frame = 0;
3432     player->StepFrame = 0;
3433
3434     player->initial_element = player->element_nr;
3435     player->artwork_element =
3436       (level.use_artwork_element[i] ? level.artwork_element[i] :
3437        player->element_nr);
3438     player->use_murphy = FALSE;
3439
3440     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3441     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3442
3443     player->gravity = level.initial_player_gravity[i];
3444
3445     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3446
3447     player->actual_frame_counter = 0;
3448
3449     player->step_counter = 0;
3450
3451     player->last_move_dir = initial_move_dir;
3452
3453     player->is_active = FALSE;
3454
3455     player->is_waiting = FALSE;
3456     player->is_moving = FALSE;
3457     player->is_auto_moving = FALSE;
3458     player->is_digging = FALSE;
3459     player->is_snapping = FALSE;
3460     player->is_collecting = FALSE;
3461     player->is_pushing = FALSE;
3462     player->is_switching = FALSE;
3463     player->is_dropping = FALSE;
3464     player->is_dropping_pressed = FALSE;
3465
3466     player->is_bored = FALSE;
3467     player->is_sleeping = FALSE;
3468
3469     player->was_waiting = TRUE;
3470     player->was_moving = FALSE;
3471     player->was_snapping = FALSE;
3472     player->was_dropping = FALSE;
3473
3474     player->force_dropping = FALSE;
3475
3476     player->frame_counter_bored = -1;
3477     player->frame_counter_sleeping = -1;
3478
3479     player->anim_delay_counter = 0;
3480     player->post_delay_counter = 0;
3481
3482     player->dir_waiting = initial_move_dir;
3483     player->action_waiting = ACTION_DEFAULT;
3484     player->last_action_waiting = ACTION_DEFAULT;
3485     player->special_action_bored = ACTION_DEFAULT;
3486     player->special_action_sleeping = ACTION_DEFAULT;
3487
3488     player->switch_x = -1;
3489     player->switch_y = -1;
3490
3491     player->drop_x = -1;
3492     player->drop_y = -1;
3493
3494     player->show_envelope = 0;
3495
3496     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3497
3498     player->push_delay       = -1;      // initialized when pushing starts
3499     player->push_delay_value = game.initial_push_delay_value;
3500
3501     player->drop_delay = 0;
3502     player->drop_pressed_delay = 0;
3503
3504     player->last_jx = -1;
3505     player->last_jy = -1;
3506     player->jx = -1;
3507     player->jy = -1;
3508
3509     player->shield_normal_time_left = 0;
3510     player->shield_deadly_time_left = 0;
3511
3512     player->inventory_infinite_element = EL_UNDEFINED;
3513     player->inventory_size = 0;
3514
3515     if (level.use_initial_inventory[i])
3516     {
3517       for (j = 0; j < level.initial_inventory_size[i]; j++)
3518       {
3519         int element = level.initial_inventory_content[i][j];
3520         int collect_count = element_info[element].collect_count_initial;
3521         int k;
3522
3523         if (!IS_CUSTOM_ELEMENT(element))
3524           collect_count = 1;
3525
3526         if (collect_count == 0)
3527           player->inventory_infinite_element = element;
3528         else
3529           for (k = 0; k < collect_count; k++)
3530             if (player->inventory_size < MAX_INVENTORY_SIZE)
3531               player->inventory_element[player->inventory_size++] = element;
3532       }
3533     }
3534
3535     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3536     SnapField(player, 0, 0);
3537
3538     map_player_action[i] = i;
3539   }
3540
3541   network_player_action_received = FALSE;
3542
3543   // initial null action
3544   if (network_playing)
3545     SendToServer_MovePlayer(MV_NONE);
3546
3547   FrameCounter = 0;
3548   TimeFrames = 0;
3549   TimePlayed = 0;
3550   TimeLeft = level.time;
3551   TapeTime = 0;
3552
3553   ScreenMovDir = MV_NONE;
3554   ScreenMovPos = 0;
3555   ScreenGfxPos = 0;
3556
3557   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3558
3559   game.robot_wheel_x = -1;
3560   game.robot_wheel_y = -1;
3561
3562   game.exit_x = -1;
3563   game.exit_y = -1;
3564
3565   game.all_players_gone = FALSE;
3566
3567   game.LevelSolved = FALSE;
3568   game.GameOver = FALSE;
3569
3570   game.GamePlayed = !tape.playing;
3571
3572   game.LevelSolved_GameWon = FALSE;
3573   game.LevelSolved_GameEnd = FALSE;
3574   game.LevelSolved_SaveTape = FALSE;
3575   game.LevelSolved_SaveScore = FALSE;
3576
3577   game.LevelSolved_CountingTime = 0;
3578   game.LevelSolved_CountingScore = 0;
3579   game.LevelSolved_CountingHealth = 0;
3580
3581   game.panel.active = TRUE;
3582
3583   game.no_time_limit = (level.time == 0);
3584
3585   game.yamyam_content_nr = 0;
3586   game.robot_wheel_active = FALSE;
3587   game.magic_wall_active = FALSE;
3588   game.magic_wall_time_left = 0;
3589   game.light_time_left = 0;
3590   game.timegate_time_left = 0;
3591   game.switchgate_pos = 0;
3592   game.wind_direction = level.wind_direction_initial;
3593
3594   game.score = 0;
3595   game.score_final = 0;
3596
3597   game.health = MAX_HEALTH;
3598   game.health_final = MAX_HEALTH;
3599
3600   game.gems_still_needed = level.gems_needed;
3601   game.sokoban_fields_still_needed = 0;
3602   game.sokoban_objects_still_needed = 0;
3603   game.lights_still_needed = 0;
3604   game.players_still_needed = 0;
3605   game.friends_still_needed = 0;
3606
3607   game.lenses_time_left = 0;
3608   game.magnify_time_left = 0;
3609
3610   game.ball_state = level.ball_state_initial;
3611   game.ball_content_nr = 0;
3612
3613   game.explosions_delayed = TRUE;
3614
3615   game.envelope_active = FALSE;
3616
3617   for (i = 0; i < NUM_BELTS; i++)
3618   {
3619     game.belt_dir[i] = MV_NONE;
3620     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3621   }
3622
3623   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3624     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3625
3626 #if DEBUG_INIT_PLAYER
3627   DebugPrintPlayerStatus("Player status at level initialization");
3628 #endif
3629
3630   SCAN_PLAYFIELD(x, y)
3631   {
3632     Feld[x][y] = Last[x][y] = level.field[x][y];
3633     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3634     ChangeDelay[x][y] = 0;
3635     ChangePage[x][y] = -1;
3636     CustomValue[x][y] = 0;              // initialized in InitField()
3637     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3638     AmoebaNr[x][y] = 0;
3639     WasJustMoving[x][y] = 0;
3640     WasJustFalling[x][y] = 0;
3641     CheckCollision[x][y] = 0;
3642     CheckImpact[x][y] = 0;
3643     Stop[x][y] = FALSE;
3644     Pushed[x][y] = FALSE;
3645
3646     ChangeCount[x][y] = 0;
3647     ChangeEvent[x][y] = -1;
3648
3649     ExplodePhase[x][y] = 0;
3650     ExplodeDelay[x][y] = 0;
3651     ExplodeField[x][y] = EX_TYPE_NONE;
3652
3653     RunnerVisit[x][y] = 0;
3654     PlayerVisit[x][y] = 0;
3655
3656     GfxFrame[x][y] = 0;
3657     GfxRandom[x][y] = INIT_GFX_RANDOM();
3658     GfxElement[x][y] = EL_UNDEFINED;
3659     GfxAction[x][y] = ACTION_DEFAULT;
3660     GfxDir[x][y] = MV_NONE;
3661     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3662   }
3663
3664   SCAN_PLAYFIELD(x, y)
3665   {
3666     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3667       emulate_bd = FALSE;
3668     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3669       emulate_sb = FALSE;
3670     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3671       emulate_sp = FALSE;
3672
3673     InitField(x, y, TRUE);
3674
3675     ResetGfxAnimation(x, y);
3676   }
3677
3678   InitBeltMovement();
3679
3680   for (i = 0; i < MAX_PLAYERS; i++)
3681   {
3682     struct PlayerInfo *player = &stored_player[i];
3683
3684     // set number of special actions for bored and sleeping animation
3685     player->num_special_action_bored =
3686       get_num_special_action(player->artwork_element,
3687                              ACTION_BORING_1, ACTION_BORING_LAST);
3688     player->num_special_action_sleeping =
3689       get_num_special_action(player->artwork_element,
3690                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3691   }
3692
3693   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3694                     emulate_sb ? EMU_SOKOBAN :
3695                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3696
3697   // initialize type of slippery elements
3698   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3699   {
3700     if (!IS_CUSTOM_ELEMENT(i))
3701     {
3702       // default: elements slip down either to the left or right randomly
3703       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3704
3705       // SP style elements prefer to slip down on the left side
3706       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3707         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3708
3709       // BD style elements prefer to slip down on the left side
3710       if (game.emulation == EMU_BOULDERDASH)
3711         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3712     }
3713   }
3714
3715   // initialize explosion and ignition delay
3716   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3717   {
3718     if (!IS_CUSTOM_ELEMENT(i))
3719     {
3720       int num_phase = 8;
3721       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3722                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3723                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3724       int last_phase = (num_phase + 1) * delay;
3725       int half_phase = (num_phase / 2) * delay;
3726
3727       element_info[i].explosion_delay = last_phase - 1;
3728       element_info[i].ignition_delay = half_phase;
3729
3730       if (i == EL_BLACK_ORB)
3731         element_info[i].ignition_delay = 1;
3732     }
3733   }
3734
3735   // correct non-moving belts to start moving left
3736   for (i = 0; i < NUM_BELTS; i++)
3737     if (game.belt_dir[i] == MV_NONE)
3738       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3739
3740 #if USE_NEW_PLAYER_ASSIGNMENTS
3741   // use preferred player also in local single-player mode
3742   if (!network.enabled && !game.team_mode)
3743   {
3744     int old_index_nr = local_player->index_nr;
3745     int new_index_nr = setup.network_player_nr;
3746
3747     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3748     {
3749       stored_player[old_index_nr].connected_locally = FALSE;
3750       stored_player[new_index_nr].connected_locally = TRUE;
3751     }
3752   }
3753
3754   for (i = 0; i < MAX_PLAYERS; i++)
3755   {
3756     stored_player[i].connected = FALSE;
3757
3758     // in network game mode, the local player might not be the first player
3759     if (stored_player[i].connected_locally)
3760       local_player = &stored_player[i];
3761   }
3762
3763   if (!network.enabled)
3764     local_player->connected = TRUE;
3765
3766   if (tape.playing)
3767   {
3768     for (i = 0; i < MAX_PLAYERS; i++)
3769       stored_player[i].connected = tape.player_participates[i];
3770   }
3771   else if (network.enabled)
3772   {
3773     // add team mode players connected over the network (needed for correct
3774     // assignment of player figures from level to locally playing players)
3775
3776     for (i = 0; i < MAX_PLAYERS; i++)
3777       if (stored_player[i].connected_network)
3778         stored_player[i].connected = TRUE;
3779   }
3780   else if (game.team_mode)
3781   {
3782     // try to guess locally connected team mode players (needed for correct
3783     // assignment of player figures from level to locally playing players)
3784
3785     for (i = 0; i < MAX_PLAYERS; i++)
3786       if (setup.input[i].use_joystick ||
3787           setup.input[i].key.left != KSYM_UNDEFINED)
3788         stored_player[i].connected = TRUE;
3789   }
3790
3791 #if DEBUG_INIT_PLAYER
3792   DebugPrintPlayerStatus("Player status after level initialization");
3793 #endif
3794
3795 #if DEBUG_INIT_PLAYER
3796   if (options.debug)
3797     printf("Reassigning players ...\n");
3798 #endif
3799
3800   // check if any connected player was not found in playfield
3801   for (i = 0; i < MAX_PLAYERS; i++)
3802   {
3803     struct PlayerInfo *player = &stored_player[i];
3804
3805     if (player->connected && !player->present)
3806     {
3807       struct PlayerInfo *field_player = NULL;
3808
3809 #if DEBUG_INIT_PLAYER
3810       if (options.debug)
3811         printf("- looking for field player for player %d ...\n", i + 1);
3812 #endif
3813
3814       // assign first free player found that is present in the playfield
3815
3816       // first try: look for unmapped playfield player that is not connected
3817       for (j = 0; j < MAX_PLAYERS; j++)
3818         if (field_player == NULL &&
3819             stored_player[j].present &&
3820             !stored_player[j].mapped &&
3821             !stored_player[j].connected)
3822           field_player = &stored_player[j];
3823
3824       // second try: look for *any* unmapped playfield player
3825       for (j = 0; j < MAX_PLAYERS; j++)
3826         if (field_player == NULL &&
3827             stored_player[j].present &&
3828             !stored_player[j].mapped)
3829           field_player = &stored_player[j];
3830
3831       if (field_player != NULL)
3832       {
3833         int jx = field_player->jx, jy = field_player->jy;
3834
3835 #if DEBUG_INIT_PLAYER
3836         if (options.debug)
3837           printf("- found player %d\n", field_player->index_nr + 1);
3838 #endif
3839
3840         player->present = FALSE;
3841         player->active = FALSE;
3842
3843         field_player->present = TRUE;
3844         field_player->active = TRUE;
3845
3846         /*
3847         player->initial_element = field_player->initial_element;
3848         player->artwork_element = field_player->artwork_element;
3849
3850         player->block_last_field       = field_player->block_last_field;
3851         player->block_delay_adjustment = field_player->block_delay_adjustment;
3852         */
3853
3854         StorePlayer[jx][jy] = field_player->element_nr;
3855
3856         field_player->jx = field_player->last_jx = jx;
3857         field_player->jy = field_player->last_jy = jy;
3858
3859         if (local_player == player)
3860           local_player = field_player;
3861
3862         map_player_action[field_player->index_nr] = i;
3863
3864         field_player->mapped = TRUE;
3865
3866 #if DEBUG_INIT_PLAYER
3867         if (options.debug)
3868           printf("- map_player_action[%d] == %d\n",
3869                  field_player->index_nr + 1, i + 1);
3870 #endif
3871       }
3872     }
3873
3874     if (player->connected && player->present)
3875       player->mapped = TRUE;
3876   }
3877
3878 #if DEBUG_INIT_PLAYER
3879   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3880 #endif
3881
3882 #else
3883
3884   // check if any connected player was not found in playfield
3885   for (i = 0; i < MAX_PLAYERS; i++)
3886   {
3887     struct PlayerInfo *player = &stored_player[i];
3888
3889     if (player->connected && !player->present)
3890     {
3891       for (j = 0; j < MAX_PLAYERS; j++)
3892       {
3893         struct PlayerInfo *field_player = &stored_player[j];
3894         int jx = field_player->jx, jy = field_player->jy;
3895
3896         // assign first free player found that is present in the playfield
3897         if (field_player->present && !field_player->connected)
3898         {
3899           player->present = TRUE;
3900           player->active = TRUE;
3901
3902           field_player->present = FALSE;
3903           field_player->active = FALSE;
3904
3905           player->initial_element = field_player->initial_element;
3906           player->artwork_element = field_player->artwork_element;
3907
3908           player->block_last_field       = field_player->block_last_field;
3909           player->block_delay_adjustment = field_player->block_delay_adjustment;
3910
3911           StorePlayer[jx][jy] = player->element_nr;
3912
3913           player->jx = player->last_jx = jx;
3914           player->jy = player->last_jy = jy;
3915
3916           break;
3917         }
3918       }
3919     }
3920   }
3921 #endif
3922
3923 #if 0
3924   printf("::: local_player->present == %d\n", local_player->present);
3925 #endif
3926
3927   // set focus to local player for network games, else to all players
3928   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3929   game.centered_player_nr_next = game.centered_player_nr;
3930   game.set_centered_player = FALSE;
3931
3932   if (network_playing && tape.recording)
3933   {
3934     // store client dependent player focus when recording network games
3935     tape.centered_player_nr_next = game.centered_player_nr_next;
3936     tape.set_centered_player = TRUE;
3937   }
3938
3939   if (tape.playing)
3940   {
3941     // when playing a tape, eliminate all players who do not participate
3942
3943 #if USE_NEW_PLAYER_ASSIGNMENTS
3944
3945     if (!game.team_mode)
3946     {
3947       for (i = 0; i < MAX_PLAYERS; i++)
3948       {
3949         if (stored_player[i].active &&
3950             !tape.player_participates[map_player_action[i]])
3951         {
3952           struct PlayerInfo *player = &stored_player[i];
3953           int jx = player->jx, jy = player->jy;
3954
3955 #if DEBUG_INIT_PLAYER
3956           if (options.debug)
3957             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3958 #endif
3959
3960           player->active = FALSE;
3961           StorePlayer[jx][jy] = 0;
3962           Feld[jx][jy] = EL_EMPTY;
3963         }
3964       }
3965     }
3966
3967 #else
3968
3969     for (i = 0; i < MAX_PLAYERS; i++)
3970     {
3971       if (stored_player[i].active &&
3972           !tape.player_participates[i])
3973       {
3974         struct PlayerInfo *player = &stored_player[i];
3975         int jx = player->jx, jy = player->jy;
3976
3977         player->active = FALSE;
3978         StorePlayer[jx][jy] = 0;
3979         Feld[jx][jy] = EL_EMPTY;
3980       }
3981     }
3982 #endif
3983   }
3984   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3985   {
3986     // when in single player mode, eliminate all but the local player
3987
3988     for (i = 0; i < MAX_PLAYERS; i++)
3989     {
3990       struct PlayerInfo *player = &stored_player[i];
3991
3992       if (player->active && player != local_player)
3993       {
3994         int jx = player->jx, jy = player->jy;
3995
3996         player->active = FALSE;
3997         player->present = FALSE;
3998
3999         StorePlayer[jx][jy] = 0;
4000         Feld[jx][jy] = EL_EMPTY;
4001       }
4002     }
4003   }
4004
4005   for (i = 0; i < MAX_PLAYERS; i++)
4006     if (stored_player[i].active)
4007       game.players_still_needed++;
4008
4009   if (level.solved_by_one_player)
4010     game.players_still_needed = 1;
4011
4012   // when recording the game, store which players take part in the game
4013   if (tape.recording)
4014   {
4015 #if USE_NEW_PLAYER_ASSIGNMENTS
4016     for (i = 0; i < MAX_PLAYERS; i++)
4017       if (stored_player[i].connected)
4018         tape.player_participates[i] = TRUE;
4019 #else
4020     for (i = 0; i < MAX_PLAYERS; i++)
4021       if (stored_player[i].active)
4022         tape.player_participates[i] = TRUE;
4023 #endif
4024   }
4025
4026 #if DEBUG_INIT_PLAYER
4027   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4028 #endif
4029
4030   if (BorderElement == EL_EMPTY)
4031   {
4032     SBX_Left = 0;
4033     SBX_Right = lev_fieldx - SCR_FIELDX;
4034     SBY_Upper = 0;
4035     SBY_Lower = lev_fieldy - SCR_FIELDY;
4036   }
4037   else
4038   {
4039     SBX_Left = -1;
4040     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4041     SBY_Upper = -1;
4042     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4043   }
4044
4045   if (full_lev_fieldx <= SCR_FIELDX)
4046     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4047   if (full_lev_fieldy <= SCR_FIELDY)
4048     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4049
4050   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4051     SBX_Left--;
4052   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4053     SBY_Upper--;
4054
4055   // if local player not found, look for custom element that might create
4056   // the player (make some assumptions about the right custom element)
4057   if (!local_player->present)
4058   {
4059     int start_x = 0, start_y = 0;
4060     int found_rating = 0;
4061     int found_element = EL_UNDEFINED;
4062     int player_nr = local_player->index_nr;
4063
4064     SCAN_PLAYFIELD(x, y)
4065     {
4066       int element = Feld[x][y];
4067       int content;
4068       int xx, yy;
4069       boolean is_player;
4070
4071       if (level.use_start_element[player_nr] &&
4072           level.start_element[player_nr] == element &&
4073           found_rating < 4)
4074       {
4075         start_x = x;
4076         start_y = y;
4077
4078         found_rating = 4;
4079         found_element = element;
4080       }
4081
4082       if (!IS_CUSTOM_ELEMENT(element))
4083         continue;
4084
4085       if (CAN_CHANGE(element))
4086       {
4087         for (i = 0; i < element_info[element].num_change_pages; i++)
4088         {
4089           // check for player created from custom element as single target
4090           content = element_info[element].change_page[i].target_element;
4091           is_player = ELEM_IS_PLAYER(content);
4092
4093           if (is_player && (found_rating < 3 ||
4094                             (found_rating == 3 && element < found_element)))
4095           {
4096             start_x = x;
4097             start_y = y;
4098
4099             found_rating = 3;
4100             found_element = element;
4101           }
4102         }
4103       }
4104
4105       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4106       {
4107         // check for player created from custom element as explosion content
4108         content = element_info[element].content.e[xx][yy];
4109         is_player = ELEM_IS_PLAYER(content);
4110
4111         if (is_player && (found_rating < 2 ||
4112                           (found_rating == 2 && element < found_element)))
4113         {
4114           start_x = x + xx - 1;
4115           start_y = y + yy - 1;
4116
4117           found_rating = 2;
4118           found_element = element;
4119         }
4120
4121         if (!CAN_CHANGE(element))
4122           continue;
4123
4124         for (i = 0; i < element_info[element].num_change_pages; i++)
4125         {
4126           // check for player created from custom element as extended target
4127           content =
4128             element_info[element].change_page[i].target_content.e[xx][yy];
4129
4130           is_player = ELEM_IS_PLAYER(content);
4131
4132           if (is_player && (found_rating < 1 ||
4133                             (found_rating == 1 && element < found_element)))
4134           {
4135             start_x = x + xx - 1;
4136             start_y = y + yy - 1;
4137
4138             found_rating = 1;
4139             found_element = element;
4140           }
4141         }
4142       }
4143     }
4144
4145     scroll_x = SCROLL_POSITION_X(start_x);
4146     scroll_y = SCROLL_POSITION_Y(start_y);
4147   }
4148   else
4149   {
4150     scroll_x = SCROLL_POSITION_X(local_player->jx);
4151     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4152   }
4153
4154   // !!! FIX THIS (START) !!!
4155   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4156   {
4157     InitGameEngine_EM();
4158   }
4159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4160   {
4161     InitGameEngine_SP();
4162   }
4163   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4164   {
4165     InitGameEngine_MM();
4166   }
4167   else
4168   {
4169     DrawLevel(REDRAW_FIELD);
4170     DrawAllPlayers();
4171
4172     // after drawing the level, correct some elements
4173     if (game.timegate_time_left == 0)
4174       CloseAllOpenTimegates();
4175   }
4176
4177   // blit playfield from scroll buffer to normal back buffer for fading in
4178   BlitScreenToBitmap(backbuffer);
4179   // !!! FIX THIS (END) !!!
4180
4181   DrawMaskedBorder(fade_mask);
4182
4183   FadeIn(fade_mask);
4184
4185 #if 1
4186   // full screen redraw is required at this point in the following cases:
4187   // - special editor door undrawn when game was started from level editor
4188   // - drawing area (playfield) was changed and has to be removed completely
4189   redraw_mask = REDRAW_ALL;
4190   BackToFront();
4191 #endif
4192
4193   if (!game.restart_level)
4194   {
4195     // copy default game door content to main double buffer
4196
4197     // !!! CHECK AGAIN !!!
4198     SetPanelBackground();
4199     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4200     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4201   }
4202
4203   SetPanelBackground();
4204   SetDrawBackgroundMask(REDRAW_DOOR_1);
4205
4206   UpdateAndDisplayGameControlValues();
4207
4208   if (!game.restart_level)
4209   {
4210     UnmapGameButtons();
4211     UnmapTapeButtons();
4212
4213     FreeGameButtons();
4214     CreateGameButtons();
4215
4216     MapGameButtons();
4217     MapTapeButtons();
4218
4219     // copy actual game door content to door double buffer for OpenDoor()
4220     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4221
4222     OpenDoor(DOOR_OPEN_ALL);
4223
4224     KeyboardAutoRepeatOffUnlessAutoplay();
4225
4226 #if DEBUG_INIT_PLAYER
4227     DebugPrintPlayerStatus("Player status (final)");
4228 #endif
4229   }
4230
4231   UnmapAllGadgets();
4232
4233   MapGameButtons();
4234   MapTapeButtons();
4235
4236   if (!game.restart_level && !tape.playing)
4237   {
4238     LevelStats_incPlayed(level_nr);
4239
4240     SaveLevelSetup_SeriesInfo();
4241   }
4242
4243   game.restart_level = FALSE;
4244   game.restart_game_message = NULL;
4245   game.request_active = FALSE;
4246
4247   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4248     InitGameActions_MM();
4249
4250   SaveEngineSnapshotToListInitial();
4251
4252   if (!game.restart_level)
4253   {
4254     PlaySound(SND_GAME_STARTING);
4255
4256     if (setup.sound_music)
4257       PlayLevelMusic();
4258   }
4259 }
4260
4261 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4262                         int actual_player_x, int actual_player_y)
4263 {
4264   // this is used for non-R'n'D game engines to update certain engine values
4265
4266   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4267   {
4268     actual_player_x = correctLevelPosX_EM(actual_player_x);
4269     actual_player_y = correctLevelPosY_EM(actual_player_y);
4270   }
4271
4272   // needed to determine if sounds are played within the visible screen area
4273   scroll_x = actual_scroll_x;
4274   scroll_y = actual_scroll_y;
4275
4276   // needed to get player position for "follow finger" playing input method
4277   local_player->jx = actual_player_x;
4278   local_player->jy = actual_player_y;
4279 }
4280
4281 void InitMovDir(int x, int y)
4282 {
4283   int i, element = Feld[x][y];
4284   static int xy[4][2] =
4285   {
4286     {  0, +1 },
4287     { +1,  0 },
4288     {  0, -1 },
4289     { -1,  0 }
4290   };
4291   static int direction[3][4] =
4292   {
4293     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4294     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4295     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4296   };
4297
4298   switch (element)
4299   {
4300     case EL_BUG_RIGHT:
4301     case EL_BUG_UP:
4302     case EL_BUG_LEFT:
4303     case EL_BUG_DOWN:
4304       Feld[x][y] = EL_BUG;
4305       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4306       break;
4307
4308     case EL_SPACESHIP_RIGHT:
4309     case EL_SPACESHIP_UP:
4310     case EL_SPACESHIP_LEFT:
4311     case EL_SPACESHIP_DOWN:
4312       Feld[x][y] = EL_SPACESHIP;
4313       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4314       break;
4315
4316     case EL_BD_BUTTERFLY_RIGHT:
4317     case EL_BD_BUTTERFLY_UP:
4318     case EL_BD_BUTTERFLY_LEFT:
4319     case EL_BD_BUTTERFLY_DOWN:
4320       Feld[x][y] = EL_BD_BUTTERFLY;
4321       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4322       break;
4323
4324     case EL_BD_FIREFLY_RIGHT:
4325     case EL_BD_FIREFLY_UP:
4326     case EL_BD_FIREFLY_LEFT:
4327     case EL_BD_FIREFLY_DOWN:
4328       Feld[x][y] = EL_BD_FIREFLY;
4329       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4330       break;
4331
4332     case EL_PACMAN_RIGHT:
4333     case EL_PACMAN_UP:
4334     case EL_PACMAN_LEFT:
4335     case EL_PACMAN_DOWN:
4336       Feld[x][y] = EL_PACMAN;
4337       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4338       break;
4339
4340     case EL_YAMYAM_LEFT:
4341     case EL_YAMYAM_RIGHT:
4342     case EL_YAMYAM_UP:
4343     case EL_YAMYAM_DOWN:
4344       Feld[x][y] = EL_YAMYAM;
4345       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4346       break;
4347
4348     case EL_SP_SNIKSNAK:
4349       MovDir[x][y] = MV_UP;
4350       break;
4351
4352     case EL_SP_ELECTRON:
4353       MovDir[x][y] = MV_LEFT;
4354       break;
4355
4356     case EL_MOLE_LEFT:
4357     case EL_MOLE_RIGHT:
4358     case EL_MOLE_UP:
4359     case EL_MOLE_DOWN:
4360       Feld[x][y] = EL_MOLE;
4361       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4362       break;
4363
4364     default:
4365       if (IS_CUSTOM_ELEMENT(element))
4366       {
4367         struct ElementInfo *ei = &element_info[element];
4368         int move_direction_initial = ei->move_direction_initial;
4369         int move_pattern = ei->move_pattern;
4370
4371         if (move_direction_initial == MV_START_PREVIOUS)
4372         {
4373           if (MovDir[x][y] != MV_NONE)
4374             return;
4375
4376           move_direction_initial = MV_START_AUTOMATIC;
4377         }
4378
4379         if (move_direction_initial == MV_START_RANDOM)
4380           MovDir[x][y] = 1 << RND(4);
4381         else if (move_direction_initial & MV_ANY_DIRECTION)
4382           MovDir[x][y] = move_direction_initial;
4383         else if (move_pattern == MV_ALL_DIRECTIONS ||
4384                  move_pattern == MV_TURNING_LEFT ||
4385                  move_pattern == MV_TURNING_RIGHT ||
4386                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4387                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4388                  move_pattern == MV_TURNING_RANDOM)
4389           MovDir[x][y] = 1 << RND(4);
4390         else if (move_pattern == MV_HORIZONTAL)
4391           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4392         else if (move_pattern == MV_VERTICAL)
4393           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4394         else if (move_pattern & MV_ANY_DIRECTION)
4395           MovDir[x][y] = element_info[element].move_pattern;
4396         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4397                  move_pattern == MV_ALONG_RIGHT_SIDE)
4398         {
4399           // use random direction as default start direction
4400           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4401             MovDir[x][y] = 1 << RND(4);
4402
4403           for (i = 0; i < NUM_DIRECTIONS; i++)
4404           {
4405             int x1 = x + xy[i][0];
4406             int y1 = y + xy[i][1];
4407
4408             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4409             {
4410               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4411                 MovDir[x][y] = direction[0][i];
4412               else
4413                 MovDir[x][y] = direction[1][i];
4414
4415               break;
4416             }
4417           }
4418         }                
4419       }
4420       else
4421       {
4422         MovDir[x][y] = 1 << RND(4);
4423
4424         if (element != EL_BUG &&
4425             element != EL_SPACESHIP &&
4426             element != EL_BD_BUTTERFLY &&
4427             element != EL_BD_FIREFLY)
4428           break;
4429
4430         for (i = 0; i < NUM_DIRECTIONS; i++)
4431         {
4432           int x1 = x + xy[i][0];
4433           int y1 = y + xy[i][1];
4434
4435           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4436           {
4437             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4438             {
4439               MovDir[x][y] = direction[0][i];
4440               break;
4441             }
4442             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4443                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4444             {
4445               MovDir[x][y] = direction[1][i];
4446               break;
4447             }
4448           }
4449         }
4450       }
4451       break;
4452   }
4453
4454   GfxDir[x][y] = MovDir[x][y];
4455 }
4456
4457 void InitAmoebaNr(int x, int y)
4458 {
4459   int i;
4460   int group_nr = AmoebeNachbarNr(x, y);
4461
4462   if (group_nr == 0)
4463   {
4464     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4465     {
4466       if (AmoebaCnt[i] == 0)
4467       {
4468         group_nr = i;
4469         break;
4470       }
4471     }
4472   }
4473
4474   AmoebaNr[x][y] = group_nr;
4475   AmoebaCnt[group_nr]++;
4476   AmoebaCnt2[group_nr]++;
4477 }
4478
4479 static void LevelSolved(void)
4480 {
4481   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4482       game.players_still_needed > 0)
4483     return;
4484
4485   game.LevelSolved = TRUE;
4486   game.GameOver = TRUE;
4487
4488   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4489                       level.native_em_level->lev->score :
4490                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4491                       game_mm.score :
4492                       game.score);
4493   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4494                        MM_HEALTH(game_mm.laser_overload_value) :
4495                        game.health);
4496
4497   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4498   game.LevelSolved_CountingScore = game.score_final;
4499   game.LevelSolved_CountingHealth = game.health_final;
4500 }
4501
4502 void GameWon(void)
4503 {
4504   static int time_count_steps;
4505   static int time, time_final;
4506   static int score, score_final;
4507   static int health, health_final;
4508   static int game_over_delay_1 = 0;
4509   static int game_over_delay_2 = 0;
4510   static int game_over_delay_3 = 0;
4511   int game_over_delay_value_1 = 50;
4512   int game_over_delay_value_2 = 25;
4513   int game_over_delay_value_3 = 50;
4514
4515   if (!game.LevelSolved_GameWon)
4516   {
4517     int i;
4518
4519     // do not start end game actions before the player stops moving (to exit)
4520     if (local_player->active && local_player->MovPos)
4521       return;
4522
4523     game.LevelSolved_GameWon = TRUE;
4524     game.LevelSolved_SaveTape = tape.recording;
4525     game.LevelSolved_SaveScore = !tape.playing;
4526
4527     if (!tape.playing)
4528     {
4529       LevelStats_incSolved(level_nr);
4530
4531       SaveLevelSetup_SeriesInfo();
4532     }
4533
4534     if (tape.auto_play)         // tape might already be stopped here
4535       tape.auto_play_level_solved = TRUE;
4536
4537     TapeStop();
4538
4539     game_over_delay_1 = 0;
4540     game_over_delay_2 = 0;
4541     game_over_delay_3 = game_over_delay_value_3;
4542
4543     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4544     score = score_final = game.score_final;
4545     health = health_final = game.health_final;
4546
4547     if (level.score[SC_TIME_BONUS] > 0)
4548     {
4549       if (TimeLeft > 0)
4550       {
4551         time_final = 0;
4552         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4553       }
4554       else if (game.no_time_limit && TimePlayed < 999)
4555       {
4556         time_final = 999;
4557         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4558       }
4559
4560       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4561
4562       game_over_delay_1 = game_over_delay_value_1;
4563
4564       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4565       {
4566         health_final = 0;
4567         score_final += health * level.score[SC_TIME_BONUS];
4568
4569         game_over_delay_2 = game_over_delay_value_2;
4570       }
4571
4572       game.score_final = score_final;
4573       game.health_final = health_final;
4574     }
4575
4576     if (level_editor_test_game)
4577     {
4578       time = time_final;
4579       score = score_final;
4580
4581       game.LevelSolved_CountingTime = time;
4582       game.LevelSolved_CountingScore = score;
4583
4584       game_panel_controls[GAME_PANEL_TIME].value = time;
4585       game_panel_controls[GAME_PANEL_SCORE].value = score;
4586
4587       DisplayGameControlValues();
4588     }
4589
4590     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4591     {
4592       // check if last player has left the level
4593       if (game.exit_x >= 0 &&
4594           game.exit_y >= 0)
4595       {
4596         int x = game.exit_x;
4597         int y = game.exit_y;
4598         int element = Feld[x][y];
4599
4600         // close exit door after last player
4601         if ((game.all_players_gone &&
4602              (element == EL_EXIT_OPEN ||
4603               element == EL_SP_EXIT_OPEN ||
4604               element == EL_STEEL_EXIT_OPEN)) ||
4605             element == EL_EM_EXIT_OPEN ||
4606             element == EL_EM_STEEL_EXIT_OPEN)
4607         {
4608
4609           Feld[x][y] =
4610             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4611              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4612              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4613              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4614              EL_EM_STEEL_EXIT_CLOSING);
4615
4616           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4617         }
4618
4619         // player disappears
4620         DrawLevelField(x, y);
4621       }
4622
4623       for (i = 0; i < MAX_PLAYERS; i++)
4624       {
4625         struct PlayerInfo *player = &stored_player[i];
4626
4627         if (player->present)
4628         {
4629           RemovePlayer(player);
4630
4631           // player disappears
4632           DrawLevelField(player->jx, player->jy);
4633         }
4634       }
4635     }
4636
4637     PlaySound(SND_GAME_WINNING);
4638   }
4639
4640   if (game_over_delay_1 > 0)
4641   {
4642     game_over_delay_1--;
4643
4644     return;
4645   }
4646
4647   if (time != time_final)
4648   {
4649     int time_to_go = ABS(time_final - time);
4650     int time_count_dir = (time < time_final ? +1 : -1);
4651
4652     if (time_to_go < time_count_steps)
4653       time_count_steps = 1;
4654
4655     time  += time_count_steps * time_count_dir;
4656     score += time_count_steps * level.score[SC_TIME_BONUS];
4657
4658     game.LevelSolved_CountingTime = time;
4659     game.LevelSolved_CountingScore = score;
4660
4661     game_panel_controls[GAME_PANEL_TIME].value = time;
4662     game_panel_controls[GAME_PANEL_SCORE].value = score;
4663
4664     DisplayGameControlValues();
4665
4666     if (time == time_final)
4667       StopSound(SND_GAME_LEVELTIME_BONUS);
4668     else if (setup.sound_loops)
4669       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4670     else
4671       PlaySound(SND_GAME_LEVELTIME_BONUS);
4672
4673     return;
4674   }
4675
4676   if (game_over_delay_2 > 0)
4677   {
4678     game_over_delay_2--;
4679
4680     return;
4681   }
4682
4683   if (health != health_final)
4684   {
4685     int health_count_dir = (health < health_final ? +1 : -1);
4686
4687     health += health_count_dir;
4688     score  += level.score[SC_TIME_BONUS];
4689
4690     game.LevelSolved_CountingHealth = health;
4691     game.LevelSolved_CountingScore = score;
4692
4693     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4694     game_panel_controls[GAME_PANEL_SCORE].value = score;
4695
4696     DisplayGameControlValues();
4697
4698     if (health == health_final)
4699       StopSound(SND_GAME_LEVELTIME_BONUS);
4700     else if (setup.sound_loops)
4701       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4702     else
4703       PlaySound(SND_GAME_LEVELTIME_BONUS);
4704
4705     return;
4706   }
4707
4708   game.panel.active = FALSE;
4709
4710   if (game_over_delay_3 > 0)
4711   {
4712     game_over_delay_3--;
4713
4714     return;
4715   }
4716
4717   GameEnd();
4718 }
4719
4720 void GameEnd(void)
4721 {
4722   // used instead of "level_nr" (needed for network games)
4723   int last_level_nr = levelset.level_nr;
4724   int hi_pos;
4725
4726   game.LevelSolved_GameEnd = TRUE;
4727
4728   if (game.LevelSolved_SaveTape)
4729   {
4730     // make sure that request dialog to save tape does not open door again
4731     if (!global.use_envelope_request)
4732       CloseDoor(DOOR_CLOSE_1);
4733
4734     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4735   }
4736
4737   // if no tape is to be saved, close both doors simultaneously
4738   CloseDoor(DOOR_CLOSE_ALL);
4739
4740   if (level_editor_test_game)
4741   {
4742     SetGameStatus(GAME_MODE_MAIN);
4743
4744     DrawMainMenu();
4745
4746     return;
4747   }
4748
4749   if (!game.LevelSolved_SaveScore)
4750   {
4751     SetGameStatus(GAME_MODE_MAIN);
4752
4753     DrawMainMenu();
4754
4755     return;
4756   }
4757
4758   if (level_nr == leveldir_current->handicap_level)
4759   {
4760     leveldir_current->handicap_level++;
4761
4762     SaveLevelSetup_SeriesInfo();
4763   }
4764
4765   if (setup.increment_levels &&
4766       level_nr < leveldir_current->last_level &&
4767       !network_playing)
4768   {
4769     level_nr++;         // advance to next level
4770     TapeErase();        // start with empty tape
4771
4772     if (setup.auto_play_next_level)
4773     {
4774       LoadLevel(level_nr);
4775
4776       SaveLevelSetup_SeriesInfo();
4777     }
4778   }
4779
4780   hi_pos = NewHiScore(last_level_nr);
4781
4782   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4783   {
4784     SetGameStatus(GAME_MODE_SCORES);
4785
4786     DrawHallOfFame(last_level_nr, hi_pos);
4787   }
4788   else if (setup.auto_play_next_level && setup.increment_levels &&
4789            last_level_nr < leveldir_current->last_level &&
4790            !network_playing)
4791   {
4792     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4793   }
4794   else
4795   {
4796     SetGameStatus(GAME_MODE_MAIN);
4797
4798     DrawMainMenu();
4799   }
4800 }
4801
4802 int NewHiScore(int level_nr)
4803 {
4804   int k, l;
4805   int position = -1;
4806   boolean one_score_entry_per_name = !program.many_scores_per_name;
4807
4808   LoadScore(level_nr);
4809
4810   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4811       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4812     return -1;
4813
4814   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4815   {
4816     if (game.score_final > highscore[k].Score)
4817     {
4818       // player has made it to the hall of fame
4819
4820       if (k < MAX_SCORE_ENTRIES - 1)
4821       {
4822         int m = MAX_SCORE_ENTRIES - 1;
4823
4824         if (one_score_entry_per_name)
4825         {
4826           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4827             if (strEqual(setup.player_name, highscore[l].Name))
4828               m = l;
4829
4830           if (m == k)   // player's new highscore overwrites his old one
4831             goto put_into_list;
4832         }
4833
4834         for (l = m; l > k; l--)
4835         {
4836           strcpy(highscore[l].Name, highscore[l - 1].Name);
4837           highscore[l].Score = highscore[l - 1].Score;
4838         }
4839       }
4840
4841       put_into_list:
4842
4843       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4844       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4845       highscore[k].Score = game.score_final;
4846       position = k;
4847
4848       break;
4849     }
4850     else if (one_score_entry_per_name &&
4851              !strncmp(setup.player_name, highscore[k].Name,
4852                       MAX_PLAYER_NAME_LEN))
4853       break;    // player already there with a higher score
4854   }
4855
4856   if (position >= 0) 
4857     SaveScore(level_nr);
4858
4859   return position;
4860 }
4861
4862 static int getElementMoveStepsizeExt(int x, int y, int direction)
4863 {
4864   int element = Feld[x][y];
4865   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4866   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4867   int horiz_move = (dx != 0);
4868   int sign = (horiz_move ? dx : dy);
4869   int step = sign * element_info[element].move_stepsize;
4870
4871   // special values for move stepsize for spring and things on conveyor belt
4872   if (horiz_move)
4873   {
4874     if (CAN_FALL(element) &&
4875         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4876       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4877     else if (element == EL_SPRING)
4878       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4879   }
4880
4881   return step;
4882 }
4883
4884 static int getElementMoveStepsize(int x, int y)
4885 {
4886   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4887 }
4888
4889 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4890 {
4891   if (player->GfxAction != action || player->GfxDir != dir)
4892   {
4893     player->GfxAction = action;
4894     player->GfxDir = dir;
4895     player->Frame = 0;
4896     player->StepFrame = 0;
4897   }
4898 }
4899
4900 static void ResetGfxFrame(int x, int y)
4901 {
4902   // profiling showed that "autotest" spends 10~20% of its time in this function
4903   if (DrawingDeactivatedField())
4904     return;
4905
4906   int element = Feld[x][y];
4907   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4908
4909   if (graphic_info[graphic].anim_global_sync)
4910     GfxFrame[x][y] = FrameCounter;
4911   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4912     GfxFrame[x][y] = CustomValue[x][y];
4913   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4914     GfxFrame[x][y] = element_info[element].collect_score;
4915   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4916     GfxFrame[x][y] = ChangeDelay[x][y];
4917 }
4918
4919 static void ResetGfxAnimation(int x, int y)
4920 {
4921   GfxAction[x][y] = ACTION_DEFAULT;
4922   GfxDir[x][y] = MovDir[x][y];
4923   GfxFrame[x][y] = 0;
4924
4925   ResetGfxFrame(x, y);
4926 }
4927
4928 static void ResetRandomAnimationValue(int x, int y)
4929 {
4930   GfxRandom[x][y] = INIT_GFX_RANDOM();
4931 }
4932
4933 static void InitMovingField(int x, int y, int direction)
4934 {
4935   int element = Feld[x][y];
4936   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4937   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4938   int newx = x + dx;
4939   int newy = y + dy;
4940   boolean is_moving_before, is_moving_after;
4941
4942   // check if element was/is moving or being moved before/after mode change
4943   is_moving_before = (WasJustMoving[x][y] != 0);
4944   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4945
4946   // reset animation only for moving elements which change direction of moving
4947   // or which just started or stopped moving
4948   // (else CEs with property "can move" / "not moving" are reset each frame)
4949   if (is_moving_before != is_moving_after ||
4950       direction != MovDir[x][y])
4951     ResetGfxAnimation(x, y);
4952
4953   MovDir[x][y] = direction;
4954   GfxDir[x][y] = direction;
4955
4956   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4957                      direction == MV_DOWN && CAN_FALL(element) ?
4958                      ACTION_FALLING : ACTION_MOVING);
4959
4960   // this is needed for CEs with property "can move" / "not moving"
4961
4962   if (is_moving_after)
4963   {
4964     if (Feld[newx][newy] == EL_EMPTY)
4965       Feld[newx][newy] = EL_BLOCKED;
4966
4967     MovDir[newx][newy] = MovDir[x][y];
4968
4969     CustomValue[newx][newy] = CustomValue[x][y];
4970
4971     GfxFrame[newx][newy] = GfxFrame[x][y];
4972     GfxRandom[newx][newy] = GfxRandom[x][y];
4973     GfxAction[newx][newy] = GfxAction[x][y];
4974     GfxDir[newx][newy] = GfxDir[x][y];
4975   }
4976 }
4977
4978 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4979 {
4980   int direction = MovDir[x][y];
4981   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4982   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4983
4984   *goes_to_x = newx;
4985   *goes_to_y = newy;
4986 }
4987
4988 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4989 {
4990   int oldx = x, oldy = y;
4991   int direction = MovDir[x][y];
4992
4993   if (direction == MV_LEFT)
4994     oldx++;
4995   else if (direction == MV_RIGHT)
4996     oldx--;
4997   else if (direction == MV_UP)
4998     oldy++;
4999   else if (direction == MV_DOWN)
5000     oldy--;
5001
5002   *comes_from_x = oldx;
5003   *comes_from_y = oldy;
5004 }
5005
5006 static int MovingOrBlocked2Element(int x, int y)
5007 {
5008   int element = Feld[x][y];
5009
5010   if (element == EL_BLOCKED)
5011   {
5012     int oldx, oldy;
5013
5014     Blocked2Moving(x, y, &oldx, &oldy);
5015     return Feld[oldx][oldy];
5016   }
5017   else
5018     return element;
5019 }
5020
5021 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5022 {
5023   // like MovingOrBlocked2Element(), but if element is moving
5024   // and (x,y) is the field the moving element is just leaving,
5025   // return EL_BLOCKED instead of the element value
5026   int element = Feld[x][y];
5027
5028   if (IS_MOVING(x, y))
5029   {
5030     if (element == EL_BLOCKED)
5031     {
5032       int oldx, oldy;
5033
5034       Blocked2Moving(x, y, &oldx, &oldy);
5035       return Feld[oldx][oldy];
5036     }
5037     else
5038       return EL_BLOCKED;
5039   }
5040   else
5041     return element;
5042 }
5043
5044 static void RemoveField(int x, int y)
5045 {
5046   Feld[x][y] = EL_EMPTY;
5047
5048   MovPos[x][y] = 0;
5049   MovDir[x][y] = 0;
5050   MovDelay[x][y] = 0;
5051
5052   CustomValue[x][y] = 0;
5053
5054   AmoebaNr[x][y] = 0;
5055   ChangeDelay[x][y] = 0;
5056   ChangePage[x][y] = -1;
5057   Pushed[x][y] = FALSE;
5058
5059   GfxElement[x][y] = EL_UNDEFINED;
5060   GfxAction[x][y] = ACTION_DEFAULT;
5061   GfxDir[x][y] = MV_NONE;
5062 }
5063
5064 static void RemoveMovingField(int x, int y)
5065 {
5066   int oldx = x, oldy = y, newx = x, newy = y;
5067   int element = Feld[x][y];
5068   int next_element = EL_UNDEFINED;
5069
5070   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5071     return;
5072
5073   if (IS_MOVING(x, y))
5074   {
5075     Moving2Blocked(x, y, &newx, &newy);
5076
5077     if (Feld[newx][newy] != EL_BLOCKED)
5078     {
5079       // element is moving, but target field is not free (blocked), but
5080       // already occupied by something different (example: acid pool);
5081       // in this case, only remove the moving field, but not the target
5082
5083       RemoveField(oldx, oldy);
5084
5085       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5086
5087       TEST_DrawLevelField(oldx, oldy);
5088
5089       return;
5090     }
5091   }
5092   else if (element == EL_BLOCKED)
5093   {
5094     Blocked2Moving(x, y, &oldx, &oldy);
5095     if (!IS_MOVING(oldx, oldy))
5096       return;
5097   }
5098
5099   if (element == EL_BLOCKED &&
5100       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5101        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5102        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5103        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5104        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5105        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5106     next_element = get_next_element(Feld[oldx][oldy]);
5107
5108   RemoveField(oldx, oldy);
5109   RemoveField(newx, newy);
5110
5111   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5112
5113   if (next_element != EL_UNDEFINED)
5114     Feld[oldx][oldy] = next_element;
5115
5116   TEST_DrawLevelField(oldx, oldy);
5117   TEST_DrawLevelField(newx, newy);
5118 }
5119
5120 void DrawDynamite(int x, int y)
5121 {
5122   int sx = SCREENX(x), sy = SCREENY(y);
5123   int graphic = el2img(Feld[x][y]);
5124   int frame;
5125
5126   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5127     return;
5128
5129   if (IS_WALKABLE_INSIDE(Back[x][y]))
5130     return;
5131
5132   if (Back[x][y])
5133     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5134   else if (Store[x][y])
5135     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5136
5137   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5138
5139   if (Back[x][y] || Store[x][y])
5140     DrawGraphicThruMask(sx, sy, graphic, frame);
5141   else
5142     DrawGraphic(sx, sy, graphic, frame);
5143 }
5144
5145 static void CheckDynamite(int x, int y)
5146 {
5147   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5148   {
5149     MovDelay[x][y]--;
5150
5151     if (MovDelay[x][y] != 0)
5152     {
5153       DrawDynamite(x, y);
5154       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5155
5156       return;
5157     }
5158   }
5159
5160   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5161
5162   Bang(x, y);
5163 }
5164
5165 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5166 {
5167   boolean num_checked_players = 0;
5168   int i;
5169
5170   for (i = 0; i < MAX_PLAYERS; i++)
5171   {
5172     if (stored_player[i].active)
5173     {
5174       int sx = stored_player[i].jx;
5175       int sy = stored_player[i].jy;
5176
5177       if (num_checked_players == 0)
5178       {
5179         *sx1 = *sx2 = sx;
5180         *sy1 = *sy2 = sy;
5181       }
5182       else
5183       {
5184         *sx1 = MIN(*sx1, sx);
5185         *sy1 = MIN(*sy1, sy);
5186         *sx2 = MAX(*sx2, sx);
5187         *sy2 = MAX(*sy2, sy);
5188       }
5189
5190       num_checked_players++;
5191     }
5192   }
5193 }
5194
5195 static boolean checkIfAllPlayersFitToScreen_RND(void)
5196 {
5197   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5198
5199   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5200
5201   return (sx2 - sx1 < SCR_FIELDX &&
5202           sy2 - sy1 < SCR_FIELDY);
5203 }
5204
5205 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5206 {
5207   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5208
5209   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5210
5211   *sx = (sx1 + sx2) / 2;
5212   *sy = (sy1 + sy2) / 2;
5213 }
5214
5215 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5216                                boolean center_screen, boolean quick_relocation)
5217 {
5218   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5219   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5220   boolean no_delay = (tape.warp_forward);
5221   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5222   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5223   int new_scroll_x, new_scroll_y;
5224
5225   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5226   {
5227     // case 1: quick relocation inside visible screen (without scrolling)
5228
5229     RedrawPlayfield();
5230
5231     return;
5232   }
5233
5234   if (!level.shifted_relocation || center_screen)
5235   {
5236     // relocation _with_ centering of screen
5237
5238     new_scroll_x = SCROLL_POSITION_X(x);
5239     new_scroll_y = SCROLL_POSITION_Y(y);
5240   }
5241   else
5242   {
5243     // relocation _without_ centering of screen
5244
5245     int center_scroll_x = SCROLL_POSITION_X(old_x);
5246     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5247     int offset_x = x + (scroll_x - center_scroll_x);
5248     int offset_y = y + (scroll_y - center_scroll_y);
5249
5250     // for new screen position, apply previous offset to center position
5251     new_scroll_x = SCROLL_POSITION_X(offset_x);
5252     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5253   }
5254
5255   if (quick_relocation)
5256   {
5257     // case 2: quick relocation (redraw without visible scrolling)
5258
5259     scroll_x = new_scroll_x;
5260     scroll_y = new_scroll_y;
5261
5262     RedrawPlayfield();
5263
5264     return;
5265   }
5266
5267   // case 3: visible relocation (with scrolling to new position)
5268
5269   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5270
5271   SetVideoFrameDelay(wait_delay_value);
5272
5273   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5274   {
5275     int dx = 0, dy = 0;
5276     int fx = FX, fy = FY;
5277
5278     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5279     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5280
5281     if (dx == 0 && dy == 0)             // no scrolling needed at all
5282       break;
5283
5284     scroll_x -= dx;
5285     scroll_y -= dy;
5286
5287     fx += dx * TILEX / 2;
5288     fy += dy * TILEY / 2;
5289
5290     ScrollLevel(dx, dy);
5291     DrawAllPlayers();
5292
5293     // scroll in two steps of half tile size to make things smoother
5294     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5295
5296     // scroll second step to align at full tile size
5297     BlitScreenToBitmap(window);
5298   }
5299
5300   DrawAllPlayers();
5301   BackToFront();
5302
5303   SetVideoFrameDelay(frame_delay_value_old);
5304 }
5305
5306 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5307 {
5308   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5309   int player_nr = GET_PLAYER_NR(el_player);
5310   struct PlayerInfo *player = &stored_player[player_nr];
5311   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5312   boolean no_delay = (tape.warp_forward);
5313   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5314   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5315   int old_jx = player->jx;
5316   int old_jy = player->jy;
5317   int old_element = Feld[old_jx][old_jy];
5318   int element = Feld[jx][jy];
5319   boolean player_relocated = (old_jx != jx || old_jy != jy);
5320
5321   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5322   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5323   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5324   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5325   int leave_side_horiz = move_dir_horiz;
5326   int leave_side_vert  = move_dir_vert;
5327   int enter_side = enter_side_horiz | enter_side_vert;
5328   int leave_side = leave_side_horiz | leave_side_vert;
5329
5330   if (player->buried)           // do not reanimate dead player
5331     return;
5332
5333   if (!player_relocated)        // no need to relocate the player
5334     return;
5335
5336   if (IS_PLAYER(jx, jy))        // player already placed at new position
5337   {
5338     RemoveField(jx, jy);        // temporarily remove newly placed player
5339     DrawLevelField(jx, jy);
5340   }
5341
5342   if (player->present)
5343   {
5344     while (player->MovPos)
5345     {
5346       ScrollPlayer(player, SCROLL_GO_ON);
5347       ScrollScreen(NULL, SCROLL_GO_ON);
5348
5349       AdvanceFrameAndPlayerCounters(player->index_nr);
5350
5351       DrawPlayer(player);
5352
5353       BackToFront_WithFrameDelay(wait_delay_value);
5354     }
5355
5356     DrawPlayer(player);         // needed here only to cleanup last field
5357     DrawLevelField(player->jx, player->jy);     // remove player graphic
5358
5359     player->is_moving = FALSE;
5360   }
5361
5362   if (IS_CUSTOM_ELEMENT(old_element))
5363     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5364                                CE_LEFT_BY_PLAYER,
5365                                player->index_bit, leave_side);
5366
5367   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5368                                       CE_PLAYER_LEAVES_X,
5369                                       player->index_bit, leave_side);
5370
5371   Feld[jx][jy] = el_player;
5372   InitPlayerField(jx, jy, el_player, TRUE);
5373
5374   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5375      possible that the relocation target field did not contain a player element,
5376      but a walkable element, to which the new player was relocated -- in this
5377      case, restore that (already initialized!) element on the player field */
5378   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5379   {
5380     Feld[jx][jy] = element;     // restore previously existing element
5381   }
5382
5383   // only visually relocate centered player
5384   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5385                      FALSE, level.instant_relocation);
5386
5387   TestIfPlayerTouchesBadThing(jx, jy);
5388   TestIfPlayerTouchesCustomElement(jx, jy);
5389
5390   if (IS_CUSTOM_ELEMENT(element))
5391     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5392                                player->index_bit, enter_side);
5393
5394   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5395                                       player->index_bit, enter_side);
5396
5397   if (player->is_switching)
5398   {
5399     /* ensure that relocation while still switching an element does not cause
5400        a new element to be treated as also switched directly after relocation
5401        (this is important for teleporter switches that teleport the player to
5402        a place where another teleporter switch is in the same direction, which
5403        would then incorrectly be treated as immediately switched before the
5404        direction key that caused the switch was released) */
5405
5406     player->switch_x += jx - old_jx;
5407     player->switch_y += jy - old_jy;
5408   }
5409 }
5410
5411 static void Explode(int ex, int ey, int phase, int mode)
5412 {
5413   int x, y;
5414   int last_phase;
5415   int border_element;
5416
5417   // !!! eliminate this variable !!!
5418   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5419
5420   if (game.explosions_delayed)
5421   {
5422     ExplodeField[ex][ey] = mode;
5423     return;
5424   }
5425
5426   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5427   {
5428     int center_element = Feld[ex][ey];
5429     int artwork_element, explosion_element;     // set these values later
5430
5431     // remove things displayed in background while burning dynamite
5432     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5433       Back[ex][ey] = 0;
5434
5435     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5436     {
5437       // put moving element to center field (and let it explode there)
5438       center_element = MovingOrBlocked2Element(ex, ey);
5439       RemoveMovingField(ex, ey);
5440       Feld[ex][ey] = center_element;
5441     }
5442
5443     // now "center_element" is finally determined -- set related values now
5444     artwork_element = center_element;           // for custom player artwork
5445     explosion_element = center_element;         // for custom player artwork
5446
5447     if (IS_PLAYER(ex, ey))
5448     {
5449       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5450
5451       artwork_element = stored_player[player_nr].artwork_element;
5452
5453       if (level.use_explosion_element[player_nr])
5454       {
5455         explosion_element = level.explosion_element[player_nr];
5456         artwork_element = explosion_element;
5457       }
5458     }
5459
5460     if (mode == EX_TYPE_NORMAL ||
5461         mode == EX_TYPE_CENTER ||
5462         mode == EX_TYPE_CROSS)
5463       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5464
5465     last_phase = element_info[explosion_element].explosion_delay + 1;
5466
5467     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5468     {
5469       int xx = x - ex + 1;
5470       int yy = y - ey + 1;
5471       int element;
5472
5473       if (!IN_LEV_FIELD(x, y) ||
5474           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5475           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5476         continue;
5477
5478       element = Feld[x][y];
5479
5480       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5481       {
5482         element = MovingOrBlocked2Element(x, y);
5483
5484         if (!IS_EXPLOSION_PROOF(element))
5485           RemoveMovingField(x, y);
5486       }
5487
5488       // indestructible elements can only explode in center (but not flames)
5489       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5490                                            mode == EX_TYPE_BORDER)) ||
5491           element == EL_FLAMES)
5492         continue;
5493
5494       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5495          behaviour, for example when touching a yamyam that explodes to rocks
5496          with active deadly shield, a rock is created under the player !!! */
5497       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5498 #if 0
5499       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5500           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5501            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5502 #else
5503       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5504 #endif
5505       {
5506         if (IS_ACTIVE_BOMB(element))
5507         {
5508           // re-activate things under the bomb like gate or penguin
5509           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5510           Back[x][y] = 0;
5511         }
5512
5513         continue;
5514       }
5515
5516       // save walkable background elements while explosion on same tile
5517       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5518           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5519         Back[x][y] = element;
5520
5521       // ignite explodable elements reached by other explosion
5522       if (element == EL_EXPLOSION)
5523         element = Store2[x][y];
5524
5525       if (AmoebaNr[x][y] &&
5526           (element == EL_AMOEBA_FULL ||
5527            element == EL_BD_AMOEBA ||
5528            element == EL_AMOEBA_GROWING))
5529       {
5530         AmoebaCnt[AmoebaNr[x][y]]--;
5531         AmoebaCnt2[AmoebaNr[x][y]]--;
5532       }
5533
5534       RemoveField(x, y);
5535
5536       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5537       {
5538         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5539
5540         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5541
5542         if (PLAYERINFO(ex, ey)->use_murphy)
5543           Store[x][y] = EL_EMPTY;
5544       }
5545
5546       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5547       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5548       else if (ELEM_IS_PLAYER(center_element))
5549         Store[x][y] = EL_EMPTY;
5550       else if (center_element == EL_YAMYAM)
5551         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5552       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5553         Store[x][y] = element_info[center_element].content.e[xx][yy];
5554 #if 1
5555       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5556       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5557       // otherwise) -- FIX THIS !!!
5558       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5559         Store[x][y] = element_info[element].content.e[1][1];
5560 #else
5561       else if (!CAN_EXPLODE(element))
5562         Store[x][y] = element_info[element].content.e[1][1];
5563 #endif
5564       else
5565         Store[x][y] = EL_EMPTY;
5566
5567       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5568           center_element == EL_AMOEBA_TO_DIAMOND)
5569         Store2[x][y] = element;
5570
5571       Feld[x][y] = EL_EXPLOSION;
5572       GfxElement[x][y] = artwork_element;
5573
5574       ExplodePhase[x][y] = 1;
5575       ExplodeDelay[x][y] = last_phase;
5576
5577       Stop[x][y] = TRUE;
5578     }
5579
5580     if (center_element == EL_YAMYAM)
5581       game.yamyam_content_nr =
5582         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5583
5584     return;
5585   }
5586
5587   if (Stop[ex][ey])
5588     return;
5589
5590   x = ex;
5591   y = ey;
5592
5593   if (phase == 1)
5594     GfxFrame[x][y] = 0;         // restart explosion animation
5595
5596   last_phase = ExplodeDelay[x][y];
5597
5598   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5599
5600   // this can happen if the player leaves an explosion just in time
5601   if (GfxElement[x][y] == EL_UNDEFINED)
5602     GfxElement[x][y] = EL_EMPTY;
5603
5604   border_element = Store2[x][y];
5605   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5606     border_element = StorePlayer[x][y];
5607
5608   if (phase == element_info[border_element].ignition_delay ||
5609       phase == last_phase)
5610   {
5611     boolean border_explosion = FALSE;
5612
5613     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5614         !PLAYER_EXPLOSION_PROTECTED(x, y))
5615     {
5616       KillPlayerUnlessExplosionProtected(x, y);
5617       border_explosion = TRUE;
5618     }
5619     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5620     {
5621       Feld[x][y] = Store2[x][y];
5622       Store2[x][y] = 0;
5623       Bang(x, y);
5624       border_explosion = TRUE;
5625     }
5626     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5627     {
5628       AmoebeUmwandeln(x, y);
5629       Store2[x][y] = 0;
5630       border_explosion = TRUE;
5631     }
5632
5633     // if an element just explodes due to another explosion (chain-reaction),
5634     // do not immediately end the new explosion when it was the last frame of
5635     // the explosion (as it would be done in the following "if"-statement!)
5636     if (border_explosion && phase == last_phase)
5637       return;
5638   }
5639
5640   if (phase == last_phase)
5641   {
5642     int element;
5643
5644     element = Feld[x][y] = Store[x][y];
5645     Store[x][y] = Store2[x][y] = 0;
5646     GfxElement[x][y] = EL_UNDEFINED;
5647
5648     // player can escape from explosions and might therefore be still alive
5649     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5650         element <= EL_PLAYER_IS_EXPLODING_4)
5651     {
5652       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5653       int explosion_element = EL_PLAYER_1 + player_nr;
5654       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5655       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5656
5657       if (level.use_explosion_element[player_nr])
5658         explosion_element = level.explosion_element[player_nr];
5659
5660       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5661                     element_info[explosion_element].content.e[xx][yy]);
5662     }
5663
5664     // restore probably existing indestructible background element
5665     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5666       element = Feld[x][y] = Back[x][y];
5667     Back[x][y] = 0;
5668
5669     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5670     GfxDir[x][y] = MV_NONE;
5671     ChangeDelay[x][y] = 0;
5672     ChangePage[x][y] = -1;
5673
5674     CustomValue[x][y] = 0;
5675
5676     InitField_WithBug2(x, y, FALSE);
5677
5678     TEST_DrawLevelField(x, y);
5679
5680     TestIfElementTouchesCustomElement(x, y);
5681
5682     if (GFX_CRUMBLED(element))
5683       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5684
5685     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5686       StorePlayer[x][y] = 0;
5687
5688     if (ELEM_IS_PLAYER(element))
5689       RelocatePlayer(x, y, element);
5690   }
5691   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5692   {
5693     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5694     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5695
5696     if (phase == delay)
5697       TEST_DrawLevelFieldCrumbled(x, y);
5698
5699     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5700     {
5701       DrawLevelElement(x, y, Back[x][y]);
5702       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5703     }
5704     else if (IS_WALKABLE_UNDER(Back[x][y]))
5705     {
5706       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5707       DrawLevelElementThruMask(x, y, Back[x][y]);
5708     }
5709     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5710       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5711   }
5712 }
5713
5714 static void DynaExplode(int ex, int ey)
5715 {
5716   int i, j;
5717   int dynabomb_element = Feld[ex][ey];
5718   int dynabomb_size = 1;
5719   boolean dynabomb_xl = FALSE;
5720   struct PlayerInfo *player;
5721   static int xy[4][2] =
5722   {
5723     { 0, -1 },
5724     { -1, 0 },
5725     { +1, 0 },
5726     { 0, +1 }
5727   };
5728
5729   if (IS_ACTIVE_BOMB(dynabomb_element))
5730   {
5731     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5732     dynabomb_size = player->dynabomb_size;
5733     dynabomb_xl = player->dynabomb_xl;
5734     player->dynabombs_left++;
5735   }
5736
5737   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5738
5739   for (i = 0; i < NUM_DIRECTIONS; i++)
5740   {
5741     for (j = 1; j <= dynabomb_size; j++)
5742     {
5743       int x = ex + j * xy[i][0];
5744       int y = ey + j * xy[i][1];
5745       int element;
5746
5747       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5748         break;
5749
5750       element = Feld[x][y];
5751
5752       // do not restart explosions of fields with active bombs
5753       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5754         continue;
5755
5756       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5757
5758       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5759           !IS_DIGGABLE(element) && !dynabomb_xl)
5760         break;
5761     }
5762   }
5763 }
5764
5765 void Bang(int x, int y)
5766 {
5767   int element = MovingOrBlocked2Element(x, y);
5768   int explosion_type = EX_TYPE_NORMAL;
5769
5770   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5771   {
5772     struct PlayerInfo *player = PLAYERINFO(x, y);
5773
5774     element = Feld[x][y] = player->initial_element;
5775
5776     if (level.use_explosion_element[player->index_nr])
5777     {
5778       int explosion_element = level.explosion_element[player->index_nr];
5779
5780       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5781         explosion_type = EX_TYPE_CROSS;
5782       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5783         explosion_type = EX_TYPE_CENTER;
5784     }
5785   }
5786
5787   switch (element)
5788   {
5789     case EL_BUG:
5790     case EL_SPACESHIP:
5791     case EL_BD_BUTTERFLY:
5792     case EL_BD_FIREFLY:
5793     case EL_YAMYAM:
5794     case EL_DARK_YAMYAM:
5795     case EL_ROBOT:
5796     case EL_PACMAN:
5797     case EL_MOLE:
5798       RaiseScoreElement(element);
5799       break;
5800
5801     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5802     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5803     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5804     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5805     case EL_DYNABOMB_INCREASE_NUMBER:
5806     case EL_DYNABOMB_INCREASE_SIZE:
5807     case EL_DYNABOMB_INCREASE_POWER:
5808       explosion_type = EX_TYPE_DYNA;
5809       break;
5810
5811     case EL_DC_LANDMINE:
5812       explosion_type = EX_TYPE_CENTER;
5813       break;
5814
5815     case EL_PENGUIN:
5816     case EL_LAMP:
5817     case EL_LAMP_ACTIVE:
5818     case EL_AMOEBA_TO_DIAMOND:
5819       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5820         explosion_type = EX_TYPE_CENTER;
5821       break;
5822
5823     default:
5824       if (element_info[element].explosion_type == EXPLODES_CROSS)
5825         explosion_type = EX_TYPE_CROSS;
5826       else if (element_info[element].explosion_type == EXPLODES_1X1)
5827         explosion_type = EX_TYPE_CENTER;
5828       break;
5829   }
5830
5831   if (explosion_type == EX_TYPE_DYNA)
5832     DynaExplode(x, y);
5833   else
5834     Explode(x, y, EX_PHASE_START, explosion_type);
5835
5836   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5837 }
5838
5839 static void SplashAcid(int x, int y)
5840 {
5841   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5842       (!IN_LEV_FIELD(x - 1, y - 2) ||
5843        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5844     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5845
5846   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5847       (!IN_LEV_FIELD(x + 1, y - 2) ||
5848        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5849     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5850
5851   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5852 }
5853
5854 static void InitBeltMovement(void)
5855 {
5856   static int belt_base_element[4] =
5857   {
5858     EL_CONVEYOR_BELT_1_LEFT,
5859     EL_CONVEYOR_BELT_2_LEFT,
5860     EL_CONVEYOR_BELT_3_LEFT,
5861     EL_CONVEYOR_BELT_4_LEFT
5862   };
5863   static int belt_base_active_element[4] =
5864   {
5865     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5866     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5867     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5868     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5869   };
5870
5871   int x, y, i, j;
5872
5873   // set frame order for belt animation graphic according to belt direction
5874   for (i = 0; i < NUM_BELTS; i++)
5875   {
5876     int belt_nr = i;
5877
5878     for (j = 0; j < NUM_BELT_PARTS; j++)
5879     {
5880       int element = belt_base_active_element[belt_nr] + j;
5881       int graphic_1 = el2img(element);
5882       int graphic_2 = el2panelimg(element);
5883
5884       if (game.belt_dir[i] == MV_LEFT)
5885       {
5886         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5887         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5888       }
5889       else
5890       {
5891         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5892         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5893       }
5894     }
5895   }
5896
5897   SCAN_PLAYFIELD(x, y)
5898   {
5899     int element = Feld[x][y];
5900
5901     for (i = 0; i < NUM_BELTS; i++)
5902     {
5903       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5904       {
5905         int e_belt_nr = getBeltNrFromBeltElement(element);
5906         int belt_nr = i;
5907
5908         if (e_belt_nr == belt_nr)
5909         {
5910           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5911
5912           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5913         }
5914       }
5915     }
5916   }
5917 }
5918
5919 static void ToggleBeltSwitch(int x, int y)
5920 {
5921   static int belt_base_element[4] =
5922   {
5923     EL_CONVEYOR_BELT_1_LEFT,
5924     EL_CONVEYOR_BELT_2_LEFT,
5925     EL_CONVEYOR_BELT_3_LEFT,
5926     EL_CONVEYOR_BELT_4_LEFT
5927   };
5928   static int belt_base_active_element[4] =
5929   {
5930     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5931     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5932     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5933     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5934   };
5935   static int belt_base_switch_element[4] =
5936   {
5937     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5938     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5939     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5940     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5941   };
5942   static int belt_move_dir[4] =
5943   {
5944     MV_LEFT,
5945     MV_NONE,
5946     MV_RIGHT,
5947     MV_NONE,
5948   };
5949
5950   int element = Feld[x][y];
5951   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5952   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5953   int belt_dir = belt_move_dir[belt_dir_nr];
5954   int xx, yy, i;
5955
5956   if (!IS_BELT_SWITCH(element))
5957     return;
5958
5959   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5960   game.belt_dir[belt_nr] = belt_dir;
5961
5962   if (belt_dir_nr == 3)
5963     belt_dir_nr = 1;
5964
5965   // set frame order for belt animation graphic according to belt direction
5966   for (i = 0; i < NUM_BELT_PARTS; i++)
5967   {
5968     int element = belt_base_active_element[belt_nr] + i;
5969     int graphic_1 = el2img(element);
5970     int graphic_2 = el2panelimg(element);
5971
5972     if (belt_dir == MV_LEFT)
5973     {
5974       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5975       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5976     }
5977     else
5978     {
5979       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5980       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5981     }
5982   }
5983
5984   SCAN_PLAYFIELD(xx, yy)
5985   {
5986     int element = Feld[xx][yy];
5987
5988     if (IS_BELT_SWITCH(element))
5989     {
5990       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5991
5992       if (e_belt_nr == belt_nr)
5993       {
5994         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5995         TEST_DrawLevelField(xx, yy);
5996       }
5997     }
5998     else if (IS_BELT(element) && belt_dir != MV_NONE)
5999     {
6000       int e_belt_nr = getBeltNrFromBeltElement(element);
6001
6002       if (e_belt_nr == belt_nr)
6003       {
6004         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6005
6006         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6007         TEST_DrawLevelField(xx, yy);
6008       }
6009     }
6010     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6011     {
6012       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6013
6014       if (e_belt_nr == belt_nr)
6015       {
6016         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6017
6018         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6019         TEST_DrawLevelField(xx, yy);
6020       }
6021     }
6022   }
6023 }
6024
6025 static void ToggleSwitchgateSwitch(int x, int y)
6026 {
6027   int xx, yy;
6028
6029   game.switchgate_pos = !game.switchgate_pos;
6030
6031   SCAN_PLAYFIELD(xx, yy)
6032   {
6033     int element = Feld[xx][yy];
6034
6035     if (element == EL_SWITCHGATE_SWITCH_UP)
6036     {
6037       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6038       TEST_DrawLevelField(xx, yy);
6039     }
6040     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6041     {
6042       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6043       TEST_DrawLevelField(xx, yy);
6044     }
6045     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6046     {
6047       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6048       TEST_DrawLevelField(xx, yy);
6049     }
6050     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6051     {
6052       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6053       TEST_DrawLevelField(xx, yy);
6054     }
6055     else if (element == EL_SWITCHGATE_OPEN ||
6056              element == EL_SWITCHGATE_OPENING)
6057     {
6058       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6059
6060       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6061     }
6062     else if (element == EL_SWITCHGATE_CLOSED ||
6063              element == EL_SWITCHGATE_CLOSING)
6064     {
6065       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6066
6067       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6068     }
6069   }
6070 }
6071
6072 static int getInvisibleActiveFromInvisibleElement(int element)
6073 {
6074   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6075           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6076           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6077           element);
6078 }
6079
6080 static int getInvisibleFromInvisibleActiveElement(int element)
6081 {
6082   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6083           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6084           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6085           element);
6086 }
6087
6088 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6089 {
6090   int x, y;
6091
6092   SCAN_PLAYFIELD(x, y)
6093   {
6094     int element = Feld[x][y];
6095
6096     if (element == EL_LIGHT_SWITCH &&
6097         game.light_time_left > 0)
6098     {
6099       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6100       TEST_DrawLevelField(x, y);
6101     }
6102     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6103              game.light_time_left == 0)
6104     {
6105       Feld[x][y] = EL_LIGHT_SWITCH;
6106       TEST_DrawLevelField(x, y);
6107     }
6108     else if (element == EL_EMC_DRIPPER &&
6109              game.light_time_left > 0)
6110     {
6111       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6112       TEST_DrawLevelField(x, y);
6113     }
6114     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6115              game.light_time_left == 0)
6116     {
6117       Feld[x][y] = EL_EMC_DRIPPER;
6118       TEST_DrawLevelField(x, y);
6119     }
6120     else if (element == EL_INVISIBLE_STEELWALL ||
6121              element == EL_INVISIBLE_WALL ||
6122              element == EL_INVISIBLE_SAND)
6123     {
6124       if (game.light_time_left > 0)
6125         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6126
6127       TEST_DrawLevelField(x, y);
6128
6129       // uncrumble neighbour fields, if needed
6130       if (element == EL_INVISIBLE_SAND)
6131         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6132     }
6133     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6134              element == EL_INVISIBLE_WALL_ACTIVE ||
6135              element == EL_INVISIBLE_SAND_ACTIVE)
6136     {
6137       if (game.light_time_left == 0)
6138         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6139
6140       TEST_DrawLevelField(x, y);
6141
6142       // re-crumble neighbour fields, if needed
6143       if (element == EL_INVISIBLE_SAND)
6144         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6145     }
6146   }
6147 }
6148
6149 static void RedrawAllInvisibleElementsForLenses(void)
6150 {
6151   int x, y;
6152
6153   SCAN_PLAYFIELD(x, y)
6154   {
6155     int element = Feld[x][y];
6156
6157     if (element == EL_EMC_DRIPPER &&
6158         game.lenses_time_left > 0)
6159     {
6160       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6161       TEST_DrawLevelField(x, y);
6162     }
6163     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6164              game.lenses_time_left == 0)
6165     {
6166       Feld[x][y] = EL_EMC_DRIPPER;
6167       TEST_DrawLevelField(x, y);
6168     }
6169     else if (element == EL_INVISIBLE_STEELWALL ||
6170              element == EL_INVISIBLE_WALL ||
6171              element == EL_INVISIBLE_SAND)
6172     {
6173       if (game.lenses_time_left > 0)
6174         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6175
6176       TEST_DrawLevelField(x, y);
6177
6178       // uncrumble neighbour fields, if needed
6179       if (element == EL_INVISIBLE_SAND)
6180         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6181     }
6182     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6183              element == EL_INVISIBLE_WALL_ACTIVE ||
6184              element == EL_INVISIBLE_SAND_ACTIVE)
6185     {
6186       if (game.lenses_time_left == 0)
6187         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6188
6189       TEST_DrawLevelField(x, y);
6190
6191       // re-crumble neighbour fields, if needed
6192       if (element == EL_INVISIBLE_SAND)
6193         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6194     }
6195   }
6196 }
6197
6198 static void RedrawAllInvisibleElementsForMagnifier(void)
6199 {
6200   int x, y;
6201
6202   SCAN_PLAYFIELD(x, y)
6203   {
6204     int element = Feld[x][y];
6205
6206     if (element == EL_EMC_FAKE_GRASS &&
6207         game.magnify_time_left > 0)
6208     {
6209       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6210       TEST_DrawLevelField(x, y);
6211     }
6212     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6213              game.magnify_time_left == 0)
6214     {
6215       Feld[x][y] = EL_EMC_FAKE_GRASS;
6216       TEST_DrawLevelField(x, y);
6217     }
6218     else if (IS_GATE_GRAY(element) &&
6219              game.magnify_time_left > 0)
6220     {
6221       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6222                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6223                     IS_EM_GATE_GRAY(element) ?
6224                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6225                     IS_EMC_GATE_GRAY(element) ?
6226                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6227                     IS_DC_GATE_GRAY(element) ?
6228                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6229                     element);
6230       TEST_DrawLevelField(x, y);
6231     }
6232     else if (IS_GATE_GRAY_ACTIVE(element) &&
6233              game.magnify_time_left == 0)
6234     {
6235       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6236                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6237                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6238                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6239                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6240                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6241                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6242                     EL_DC_GATE_WHITE_GRAY :
6243                     element);
6244       TEST_DrawLevelField(x, y);
6245     }
6246   }
6247 }
6248
6249 static void ToggleLightSwitch(int x, int y)
6250 {
6251   int element = Feld[x][y];
6252
6253   game.light_time_left =
6254     (element == EL_LIGHT_SWITCH ?
6255      level.time_light * FRAMES_PER_SECOND : 0);
6256
6257   RedrawAllLightSwitchesAndInvisibleElements();
6258 }
6259
6260 static void ActivateTimegateSwitch(int x, int y)
6261 {
6262   int xx, yy;
6263
6264   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6265
6266   SCAN_PLAYFIELD(xx, yy)
6267   {
6268     int element = Feld[xx][yy];
6269
6270     if (element == EL_TIMEGATE_CLOSED ||
6271         element == EL_TIMEGATE_CLOSING)
6272     {
6273       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6274       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6275     }
6276
6277     /*
6278     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6279     {
6280       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6281       TEST_DrawLevelField(xx, yy);
6282     }
6283     */
6284
6285   }
6286
6287   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6288                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6289 }
6290
6291 static void Impact(int x, int y)
6292 {
6293   boolean last_line = (y == lev_fieldy - 1);
6294   boolean object_hit = FALSE;
6295   boolean impact = (last_line || object_hit);
6296   int element = Feld[x][y];
6297   int smashed = EL_STEELWALL;
6298
6299   if (!last_line)       // check if element below was hit
6300   {
6301     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6302       return;
6303
6304     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6305                                          MovDir[x][y + 1] != MV_DOWN ||
6306                                          MovPos[x][y + 1] <= TILEY / 2));
6307
6308     // do not smash moving elements that left the smashed field in time
6309     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6310         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6311       object_hit = FALSE;
6312
6313 #if USE_QUICKSAND_IMPACT_BUGFIX
6314     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6315     {
6316       RemoveMovingField(x, y + 1);
6317       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6318       Feld[x][y + 2] = EL_ROCK;
6319       TEST_DrawLevelField(x, y + 2);
6320
6321       object_hit = TRUE;
6322     }
6323
6324     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6325     {
6326       RemoveMovingField(x, y + 1);
6327       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6328       Feld[x][y + 2] = EL_ROCK;
6329       TEST_DrawLevelField(x, y + 2);
6330
6331       object_hit = TRUE;
6332     }
6333 #endif
6334
6335     if (object_hit)
6336       smashed = MovingOrBlocked2Element(x, y + 1);
6337
6338     impact = (last_line || object_hit);
6339   }
6340
6341   if (!last_line && smashed == EL_ACID) // element falls into acid
6342   {
6343     SplashAcid(x, y + 1);
6344     return;
6345   }
6346
6347   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6348   // only reset graphic animation if graphic really changes after impact
6349   if (impact &&
6350       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6351   {
6352     ResetGfxAnimation(x, y);
6353     TEST_DrawLevelField(x, y);
6354   }
6355
6356   if (impact && CAN_EXPLODE_IMPACT(element))
6357   {
6358     Bang(x, y);
6359     return;
6360   }
6361   else if (impact && element == EL_PEARL &&
6362            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6363   {
6364     ResetGfxAnimation(x, y);
6365
6366     Feld[x][y] = EL_PEARL_BREAKING;
6367     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6368     return;
6369   }
6370   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6371   {
6372     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6373
6374     return;
6375   }
6376
6377   if (impact && element == EL_AMOEBA_DROP)
6378   {
6379     if (object_hit && IS_PLAYER(x, y + 1))
6380       KillPlayerUnlessEnemyProtected(x, y + 1);
6381     else if (object_hit && smashed == EL_PENGUIN)
6382       Bang(x, y + 1);
6383     else
6384     {
6385       Feld[x][y] = EL_AMOEBA_GROWING;
6386       Store[x][y] = EL_AMOEBA_WET;
6387
6388       ResetRandomAnimationValue(x, y);
6389     }
6390     return;
6391   }
6392
6393   if (object_hit)               // check which object was hit
6394   {
6395     if ((CAN_PASS_MAGIC_WALL(element) && 
6396          (smashed == EL_MAGIC_WALL ||
6397           smashed == EL_BD_MAGIC_WALL)) ||
6398         (CAN_PASS_DC_MAGIC_WALL(element) &&
6399          smashed == EL_DC_MAGIC_WALL))
6400     {
6401       int xx, yy;
6402       int activated_magic_wall =
6403         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6404          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6405          EL_DC_MAGIC_WALL_ACTIVE);
6406
6407       // activate magic wall / mill
6408       SCAN_PLAYFIELD(xx, yy)
6409       {
6410         if (Feld[xx][yy] == smashed)
6411           Feld[xx][yy] = activated_magic_wall;
6412       }
6413
6414       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6415       game.magic_wall_active = TRUE;
6416
6417       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6418                             SND_MAGIC_WALL_ACTIVATING :
6419                             smashed == EL_BD_MAGIC_WALL ?
6420                             SND_BD_MAGIC_WALL_ACTIVATING :
6421                             SND_DC_MAGIC_WALL_ACTIVATING));
6422     }
6423
6424     if (IS_PLAYER(x, y + 1))
6425     {
6426       if (CAN_SMASH_PLAYER(element))
6427       {
6428         KillPlayerUnlessEnemyProtected(x, y + 1);
6429         return;
6430       }
6431     }
6432     else if (smashed == EL_PENGUIN)
6433     {
6434       if (CAN_SMASH_PLAYER(element))
6435       {
6436         Bang(x, y + 1);
6437         return;
6438       }
6439     }
6440     else if (element == EL_BD_DIAMOND)
6441     {
6442       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6443       {
6444         Bang(x, y + 1);
6445         return;
6446       }
6447     }
6448     else if (((element == EL_SP_INFOTRON ||
6449                element == EL_SP_ZONK) &&
6450               (smashed == EL_SP_SNIKSNAK ||
6451                smashed == EL_SP_ELECTRON ||
6452                smashed == EL_SP_DISK_ORANGE)) ||
6453              (element == EL_SP_INFOTRON &&
6454               smashed == EL_SP_DISK_YELLOW))
6455     {
6456       Bang(x, y + 1);
6457       return;
6458     }
6459     else if (CAN_SMASH_EVERYTHING(element))
6460     {
6461       if (IS_CLASSIC_ENEMY(smashed) ||
6462           CAN_EXPLODE_SMASHED(smashed))
6463       {
6464         Bang(x, y + 1);
6465         return;
6466       }
6467       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6468       {
6469         if (smashed == EL_LAMP ||
6470             smashed == EL_LAMP_ACTIVE)
6471         {
6472           Bang(x, y + 1);
6473           return;
6474         }
6475         else if (smashed == EL_NUT)
6476         {
6477           Feld[x][y + 1] = EL_NUT_BREAKING;
6478           PlayLevelSound(x, y, SND_NUT_BREAKING);
6479           RaiseScoreElement(EL_NUT);
6480           return;
6481         }
6482         else if (smashed == EL_PEARL)
6483         {
6484           ResetGfxAnimation(x, y);
6485
6486           Feld[x][y + 1] = EL_PEARL_BREAKING;
6487           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6488           return;
6489         }
6490         else if (smashed == EL_DIAMOND)
6491         {
6492           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6493           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6494           return;
6495         }
6496         else if (IS_BELT_SWITCH(smashed))
6497         {
6498           ToggleBeltSwitch(x, y + 1);
6499         }
6500         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6501                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6502                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6503                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6504         {
6505           ToggleSwitchgateSwitch(x, y + 1);
6506         }
6507         else if (smashed == EL_LIGHT_SWITCH ||
6508                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6509         {
6510           ToggleLightSwitch(x, y + 1);
6511         }
6512         else
6513         {
6514           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6515
6516           CheckElementChangeBySide(x, y + 1, smashed, element,
6517                                    CE_SWITCHED, CH_SIDE_TOP);
6518           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6519                                             CH_SIDE_TOP);
6520         }
6521       }
6522       else
6523       {
6524         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6525       }
6526     }
6527   }
6528
6529   // play sound of magic wall / mill
6530   if (!last_line &&
6531       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6532        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6533        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6534   {
6535     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6536       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6537     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6538       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6539     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6540       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6541
6542     return;
6543   }
6544
6545   // play sound of object that hits the ground
6546   if (last_line || object_hit)
6547     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6548 }
6549
6550 static void TurnRoundExt(int x, int y)
6551 {
6552   static struct
6553   {
6554     int dx, dy;
6555   } move_xy[] =
6556   {
6557     {  0,  0 },
6558     { -1,  0 },
6559     { +1,  0 },
6560     {  0,  0 },
6561     {  0, -1 },
6562     {  0,  0 }, { 0, 0 }, { 0, 0 },
6563     {  0, +1 }
6564   };
6565   static struct
6566   {
6567     int left, right, back;
6568   } turn[] =
6569   {
6570     { 0,        0,              0        },
6571     { MV_DOWN,  MV_UP,          MV_RIGHT },
6572     { MV_UP,    MV_DOWN,        MV_LEFT  },
6573     { 0,        0,              0        },
6574     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6575     { 0,        0,              0        },
6576     { 0,        0,              0        },
6577     { 0,        0,              0        },
6578     { MV_RIGHT, MV_LEFT,        MV_UP    }
6579   };
6580
6581   int element = Feld[x][y];
6582   int move_pattern = element_info[element].move_pattern;
6583
6584   int old_move_dir = MovDir[x][y];
6585   int left_dir  = turn[old_move_dir].left;
6586   int right_dir = turn[old_move_dir].right;
6587   int back_dir  = turn[old_move_dir].back;
6588
6589   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6590   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6591   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6592   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6593
6594   int left_x  = x + left_dx,  left_y  = y + left_dy;
6595   int right_x = x + right_dx, right_y = y + right_dy;
6596   int move_x  = x + move_dx,  move_y  = y + move_dy;
6597
6598   int xx, yy;
6599
6600   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6601   {
6602     TestIfBadThingTouchesOtherBadThing(x, y);
6603
6604     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6605       MovDir[x][y] = right_dir;
6606     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6607       MovDir[x][y] = left_dir;
6608
6609     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6610       MovDelay[x][y] = 9;
6611     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6612       MovDelay[x][y] = 1;
6613   }
6614   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6615   {
6616     TestIfBadThingTouchesOtherBadThing(x, y);
6617
6618     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6619       MovDir[x][y] = left_dir;
6620     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6621       MovDir[x][y] = right_dir;
6622
6623     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6624       MovDelay[x][y] = 9;
6625     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6626       MovDelay[x][y] = 1;
6627   }
6628   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6629   {
6630     TestIfBadThingTouchesOtherBadThing(x, y);
6631
6632     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6633       MovDir[x][y] = left_dir;
6634     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6635       MovDir[x][y] = right_dir;
6636
6637     if (MovDir[x][y] != old_move_dir)
6638       MovDelay[x][y] = 9;
6639   }
6640   else if (element == EL_YAMYAM)
6641   {
6642     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6643     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6644
6645     if (can_turn_left && can_turn_right)
6646       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6647     else if (can_turn_left)
6648       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6649     else if (can_turn_right)
6650       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6651     else
6652       MovDir[x][y] = back_dir;
6653
6654     MovDelay[x][y] = 16 + 16 * RND(3);
6655   }
6656   else if (element == EL_DARK_YAMYAM)
6657   {
6658     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6659                                                          left_x, left_y);
6660     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6661                                                          right_x, right_y);
6662
6663     if (can_turn_left && can_turn_right)
6664       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6665     else if (can_turn_left)
6666       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6667     else if (can_turn_right)
6668       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6669     else
6670       MovDir[x][y] = back_dir;
6671
6672     MovDelay[x][y] = 16 + 16 * RND(3);
6673   }
6674   else if (element == EL_PACMAN)
6675   {
6676     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6677     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6678
6679     if (can_turn_left && can_turn_right)
6680       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6681     else if (can_turn_left)
6682       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6683     else if (can_turn_right)
6684       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6685     else
6686       MovDir[x][y] = back_dir;
6687
6688     MovDelay[x][y] = 6 + RND(40);
6689   }
6690   else if (element == EL_PIG)
6691   {
6692     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6693     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6694     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6695     boolean should_turn_left, should_turn_right, should_move_on;
6696     int rnd_value = 24;
6697     int rnd = RND(rnd_value);
6698
6699     should_turn_left = (can_turn_left &&
6700                         (!can_move_on ||
6701                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6702                                                    y + back_dy + left_dy)));
6703     should_turn_right = (can_turn_right &&
6704                          (!can_move_on ||
6705                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6706                                                     y + back_dy + right_dy)));
6707     should_move_on = (can_move_on &&
6708                       (!can_turn_left ||
6709                        !can_turn_right ||
6710                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6711                                                  y + move_dy + left_dy) ||
6712                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6713                                                  y + move_dy + right_dy)));
6714
6715     if (should_turn_left || should_turn_right || should_move_on)
6716     {
6717       if (should_turn_left && should_turn_right && should_move_on)
6718         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6719                         rnd < 2 * rnd_value / 3 ? right_dir :
6720                         old_move_dir);
6721       else if (should_turn_left && should_turn_right)
6722         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6723       else if (should_turn_left && should_move_on)
6724         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6725       else if (should_turn_right && should_move_on)
6726         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6727       else if (should_turn_left)
6728         MovDir[x][y] = left_dir;
6729       else if (should_turn_right)
6730         MovDir[x][y] = right_dir;
6731       else if (should_move_on)
6732         MovDir[x][y] = old_move_dir;
6733     }
6734     else if (can_move_on && rnd > rnd_value / 8)
6735       MovDir[x][y] = old_move_dir;
6736     else if (can_turn_left && can_turn_right)
6737       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6738     else if (can_turn_left && rnd > rnd_value / 8)
6739       MovDir[x][y] = left_dir;
6740     else if (can_turn_right && rnd > rnd_value/8)
6741       MovDir[x][y] = right_dir;
6742     else
6743       MovDir[x][y] = back_dir;
6744
6745     xx = x + move_xy[MovDir[x][y]].dx;
6746     yy = y + move_xy[MovDir[x][y]].dy;
6747
6748     if (!IN_LEV_FIELD(xx, yy) ||
6749         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6750       MovDir[x][y] = old_move_dir;
6751
6752     MovDelay[x][y] = 0;
6753   }
6754   else if (element == EL_DRAGON)
6755   {
6756     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6757     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6758     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6759     int rnd_value = 24;
6760     int rnd = RND(rnd_value);
6761
6762     if (can_move_on && rnd > rnd_value / 8)
6763       MovDir[x][y] = old_move_dir;
6764     else if (can_turn_left && can_turn_right)
6765       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6766     else if (can_turn_left && rnd > rnd_value / 8)
6767       MovDir[x][y] = left_dir;
6768     else if (can_turn_right && rnd > rnd_value / 8)
6769       MovDir[x][y] = right_dir;
6770     else
6771       MovDir[x][y] = back_dir;
6772
6773     xx = x + move_xy[MovDir[x][y]].dx;
6774     yy = y + move_xy[MovDir[x][y]].dy;
6775
6776     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6777       MovDir[x][y] = old_move_dir;
6778
6779     MovDelay[x][y] = 0;
6780   }
6781   else if (element == EL_MOLE)
6782   {
6783     boolean can_move_on =
6784       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6785                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6786                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6787     if (!can_move_on)
6788     {
6789       boolean can_turn_left =
6790         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6791                               IS_AMOEBOID(Feld[left_x][left_y])));
6792
6793       boolean can_turn_right =
6794         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6795                               IS_AMOEBOID(Feld[right_x][right_y])));
6796
6797       if (can_turn_left && can_turn_right)
6798         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6799       else if (can_turn_left)
6800         MovDir[x][y] = left_dir;
6801       else
6802         MovDir[x][y] = right_dir;
6803     }
6804
6805     if (MovDir[x][y] != old_move_dir)
6806       MovDelay[x][y] = 9;
6807   }
6808   else if (element == EL_BALLOON)
6809   {
6810     MovDir[x][y] = game.wind_direction;
6811     MovDelay[x][y] = 0;
6812   }
6813   else if (element == EL_SPRING)
6814   {
6815     if (MovDir[x][y] & MV_HORIZONTAL)
6816     {
6817       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6818           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6819       {
6820         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6821         ResetGfxAnimation(move_x, move_y);
6822         TEST_DrawLevelField(move_x, move_y);
6823
6824         MovDir[x][y] = back_dir;
6825       }
6826       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6827                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6828         MovDir[x][y] = MV_NONE;
6829     }
6830
6831     MovDelay[x][y] = 0;
6832   }
6833   else if (element == EL_ROBOT ||
6834            element == EL_SATELLITE ||
6835            element == EL_PENGUIN ||
6836            element == EL_EMC_ANDROID)
6837   {
6838     int attr_x = -1, attr_y = -1;
6839
6840     if (game.all_players_gone)
6841     {
6842       attr_x = game.exit_x;
6843       attr_y = game.exit_y;
6844     }
6845     else
6846     {
6847       int i;
6848
6849       for (i = 0; i < MAX_PLAYERS; i++)
6850       {
6851         struct PlayerInfo *player = &stored_player[i];
6852         int jx = player->jx, jy = player->jy;
6853
6854         if (!player->active)
6855           continue;
6856
6857         if (attr_x == -1 ||
6858             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6859         {
6860           attr_x = jx;
6861           attr_y = jy;
6862         }
6863       }
6864     }
6865
6866     if (element == EL_ROBOT &&
6867         game.robot_wheel_x >= 0 &&
6868         game.robot_wheel_y >= 0 &&
6869         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6870          game.engine_version < VERSION_IDENT(3,1,0,0)))
6871     {
6872       attr_x = game.robot_wheel_x;
6873       attr_y = game.robot_wheel_y;
6874     }
6875
6876     if (element == EL_PENGUIN)
6877     {
6878       int i;
6879       static int xy[4][2] =
6880       {
6881         { 0, -1 },
6882         { -1, 0 },
6883         { +1, 0 },
6884         { 0, +1 }
6885       };
6886
6887       for (i = 0; i < NUM_DIRECTIONS; i++)
6888       {
6889         int ex = x + xy[i][0];
6890         int ey = y + xy[i][1];
6891
6892         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6893                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6894                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6895                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6896         {
6897           attr_x = ex;
6898           attr_y = ey;
6899           break;
6900         }
6901       }
6902     }
6903
6904     MovDir[x][y] = MV_NONE;
6905     if (attr_x < x)
6906       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6907     else if (attr_x > x)
6908       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6909     if (attr_y < y)
6910       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6911     else if (attr_y > y)
6912       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6913
6914     if (element == EL_ROBOT)
6915     {
6916       int newx, newy;
6917
6918       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6919         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6920       Moving2Blocked(x, y, &newx, &newy);
6921
6922       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6923         MovDelay[x][y] = 8 + 8 * !RND(3);
6924       else
6925         MovDelay[x][y] = 16;
6926     }
6927     else if (element == EL_PENGUIN)
6928     {
6929       int newx, newy;
6930
6931       MovDelay[x][y] = 1;
6932
6933       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6934       {
6935         boolean first_horiz = RND(2);
6936         int new_move_dir = MovDir[x][y];
6937
6938         MovDir[x][y] =
6939           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6940         Moving2Blocked(x, y, &newx, &newy);
6941
6942         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6943           return;
6944
6945         MovDir[x][y] =
6946           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6947         Moving2Blocked(x, y, &newx, &newy);
6948
6949         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6950           return;
6951
6952         MovDir[x][y] = old_move_dir;
6953         return;
6954       }
6955     }
6956     else if (element == EL_SATELLITE)
6957     {
6958       int newx, newy;
6959
6960       MovDelay[x][y] = 1;
6961
6962       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6963       {
6964         boolean first_horiz = RND(2);
6965         int new_move_dir = MovDir[x][y];
6966
6967         MovDir[x][y] =
6968           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6969         Moving2Blocked(x, y, &newx, &newy);
6970
6971         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6972           return;
6973
6974         MovDir[x][y] =
6975           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6976         Moving2Blocked(x, y, &newx, &newy);
6977
6978         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6979           return;
6980
6981         MovDir[x][y] = old_move_dir;
6982         return;
6983       }
6984     }
6985     else if (element == EL_EMC_ANDROID)
6986     {
6987       static int check_pos[16] =
6988       {
6989         -1,             //  0 => (invalid)
6990         7,              //  1 => MV_LEFT
6991         3,              //  2 => MV_RIGHT
6992         -1,             //  3 => (invalid)
6993         1,              //  4 =>            MV_UP
6994         0,              //  5 => MV_LEFT  | MV_UP
6995         2,              //  6 => MV_RIGHT | MV_UP
6996         -1,             //  7 => (invalid)
6997         5,              //  8 =>            MV_DOWN
6998         6,              //  9 => MV_LEFT  | MV_DOWN
6999         4,              // 10 => MV_RIGHT | MV_DOWN
7000         -1,             // 11 => (invalid)
7001         -1,             // 12 => (invalid)
7002         -1,             // 13 => (invalid)
7003         -1,             // 14 => (invalid)
7004         -1,             // 15 => (invalid)
7005       };
7006       static struct
7007       {
7008         int dx, dy;
7009         int dir;
7010       } check_xy[8] =
7011       {
7012         { -1, -1,       MV_LEFT  | MV_UP   },
7013         {  0, -1,                  MV_UP   },
7014         { +1, -1,       MV_RIGHT | MV_UP   },
7015         { +1,  0,       MV_RIGHT           },
7016         { +1, +1,       MV_RIGHT | MV_DOWN },
7017         {  0, +1,                  MV_DOWN },
7018         { -1, +1,       MV_LEFT  | MV_DOWN },
7019         { -1,  0,       MV_LEFT            },
7020       };
7021       int start_pos, check_order;
7022       boolean can_clone = FALSE;
7023       int i;
7024
7025       // check if there is any free field around current position
7026       for (i = 0; i < 8; i++)
7027       {
7028         int newx = x + check_xy[i].dx;
7029         int newy = y + check_xy[i].dy;
7030
7031         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7032         {
7033           can_clone = TRUE;
7034
7035           break;
7036         }
7037       }
7038
7039       if (can_clone)            // randomly find an element to clone
7040       {
7041         can_clone = FALSE;
7042
7043         start_pos = check_pos[RND(8)];
7044         check_order = (RND(2) ? -1 : +1);
7045
7046         for (i = 0; i < 8; i++)
7047         {
7048           int pos_raw = start_pos + i * check_order;
7049           int pos = (pos_raw + 8) % 8;
7050           int newx = x + check_xy[pos].dx;
7051           int newy = y + check_xy[pos].dy;
7052
7053           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7054           {
7055             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7056             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7057
7058             Store[x][y] = Feld[newx][newy];
7059
7060             can_clone = TRUE;
7061
7062             break;
7063           }
7064         }
7065       }
7066
7067       if (can_clone)            // randomly find a direction to move
7068       {
7069         can_clone = FALSE;
7070
7071         start_pos = check_pos[RND(8)];
7072         check_order = (RND(2) ? -1 : +1);
7073
7074         for (i = 0; i < 8; i++)
7075         {
7076           int pos_raw = start_pos + i * check_order;
7077           int pos = (pos_raw + 8) % 8;
7078           int newx = x + check_xy[pos].dx;
7079           int newy = y + check_xy[pos].dy;
7080           int new_move_dir = check_xy[pos].dir;
7081
7082           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7083           {
7084             MovDir[x][y] = new_move_dir;
7085             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7086
7087             can_clone = TRUE;
7088
7089             break;
7090           }
7091         }
7092       }
7093
7094       if (can_clone)            // cloning and moving successful
7095         return;
7096
7097       // cannot clone -- try to move towards player
7098
7099       start_pos = check_pos[MovDir[x][y] & 0x0f];
7100       check_order = (RND(2) ? -1 : +1);
7101
7102       for (i = 0; i < 3; i++)
7103       {
7104         // first check start_pos, then previous/next or (next/previous) pos
7105         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7106         int pos = (pos_raw + 8) % 8;
7107         int newx = x + check_xy[pos].dx;
7108         int newy = y + check_xy[pos].dy;
7109         int new_move_dir = check_xy[pos].dir;
7110
7111         if (IS_PLAYER(newx, newy))
7112           break;
7113
7114         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7115         {
7116           MovDir[x][y] = new_move_dir;
7117           MovDelay[x][y] = level.android_move_time * 8 + 1;
7118
7119           break;
7120         }
7121       }
7122     }
7123   }
7124   else if (move_pattern == MV_TURNING_LEFT ||
7125            move_pattern == MV_TURNING_RIGHT ||
7126            move_pattern == MV_TURNING_LEFT_RIGHT ||
7127            move_pattern == MV_TURNING_RIGHT_LEFT ||
7128            move_pattern == MV_TURNING_RANDOM ||
7129            move_pattern == MV_ALL_DIRECTIONS)
7130   {
7131     boolean can_turn_left =
7132       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7133     boolean can_turn_right =
7134       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7135
7136     if (element_info[element].move_stepsize == 0)       // "not moving"
7137       return;
7138
7139     if (move_pattern == MV_TURNING_LEFT)
7140       MovDir[x][y] = left_dir;
7141     else if (move_pattern == MV_TURNING_RIGHT)
7142       MovDir[x][y] = right_dir;
7143     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7144       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7145     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7146       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7147     else if (move_pattern == MV_TURNING_RANDOM)
7148       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7149                       can_turn_right && !can_turn_left ? right_dir :
7150                       RND(2) ? left_dir : right_dir);
7151     else if (can_turn_left && can_turn_right)
7152       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7153     else if (can_turn_left)
7154       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7155     else if (can_turn_right)
7156       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7157     else
7158       MovDir[x][y] = back_dir;
7159
7160     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7161   }
7162   else if (move_pattern == MV_HORIZONTAL ||
7163            move_pattern == MV_VERTICAL)
7164   {
7165     if (move_pattern & old_move_dir)
7166       MovDir[x][y] = back_dir;
7167     else if (move_pattern == MV_HORIZONTAL)
7168       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7169     else if (move_pattern == MV_VERTICAL)
7170       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7171
7172     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7173   }
7174   else if (move_pattern & MV_ANY_DIRECTION)
7175   {
7176     MovDir[x][y] = move_pattern;
7177     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7178   }
7179   else if (move_pattern & MV_WIND_DIRECTION)
7180   {
7181     MovDir[x][y] = game.wind_direction;
7182     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7183   }
7184   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7185   {
7186     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7187       MovDir[x][y] = left_dir;
7188     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7189       MovDir[x][y] = right_dir;
7190
7191     if (MovDir[x][y] != old_move_dir)
7192       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7193   }
7194   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7195   {
7196     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7197       MovDir[x][y] = right_dir;
7198     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7199       MovDir[x][y] = left_dir;
7200
7201     if (MovDir[x][y] != old_move_dir)
7202       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7203   }
7204   else if (move_pattern == MV_TOWARDS_PLAYER ||
7205            move_pattern == MV_AWAY_FROM_PLAYER)
7206   {
7207     int attr_x = -1, attr_y = -1;
7208     int newx, newy;
7209     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7210
7211     if (game.all_players_gone)
7212     {
7213       attr_x = game.exit_x;
7214       attr_y = game.exit_y;
7215     }
7216     else
7217     {
7218       int i;
7219
7220       for (i = 0; i < MAX_PLAYERS; i++)
7221       {
7222         struct PlayerInfo *player = &stored_player[i];
7223         int jx = player->jx, jy = player->jy;
7224
7225         if (!player->active)
7226           continue;
7227
7228         if (attr_x == -1 ||
7229             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7230         {
7231           attr_x = jx;
7232           attr_y = jy;
7233         }
7234       }
7235     }
7236
7237     MovDir[x][y] = MV_NONE;
7238     if (attr_x < x)
7239       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7240     else if (attr_x > x)
7241       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7242     if (attr_y < y)
7243       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7244     else if (attr_y > y)
7245       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7246
7247     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7248
7249     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7250     {
7251       boolean first_horiz = RND(2);
7252       int new_move_dir = MovDir[x][y];
7253
7254       if (element_info[element].move_stepsize == 0)     // "not moving"
7255       {
7256         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7257         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7258
7259         return;
7260       }
7261
7262       MovDir[x][y] =
7263         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7264       Moving2Blocked(x, y, &newx, &newy);
7265
7266       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7267         return;
7268
7269       MovDir[x][y] =
7270         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7271       Moving2Blocked(x, y, &newx, &newy);
7272
7273       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7274         return;
7275
7276       MovDir[x][y] = old_move_dir;
7277     }
7278   }
7279   else if (move_pattern == MV_WHEN_PUSHED ||
7280            move_pattern == MV_WHEN_DROPPED)
7281   {
7282     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7283       MovDir[x][y] = MV_NONE;
7284
7285     MovDelay[x][y] = 0;
7286   }
7287   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7288   {
7289     static int test_xy[7][2] =
7290     {
7291       { 0, -1 },
7292       { -1, 0 },
7293       { +1, 0 },
7294       { 0, +1 },
7295       { 0, -1 },
7296       { -1, 0 },
7297       { +1, 0 },
7298     };
7299     static int test_dir[7] =
7300     {
7301       MV_UP,
7302       MV_LEFT,
7303       MV_RIGHT,
7304       MV_DOWN,
7305       MV_UP,
7306       MV_LEFT,
7307       MV_RIGHT,
7308     };
7309     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7310     int move_preference = -1000000;     // start with very low preference
7311     int new_move_dir = MV_NONE;
7312     int start_test = RND(4);
7313     int i;
7314
7315     for (i = 0; i < NUM_DIRECTIONS; i++)
7316     {
7317       int move_dir = test_dir[start_test + i];
7318       int move_dir_preference;
7319
7320       xx = x + test_xy[start_test + i][0];
7321       yy = y + test_xy[start_test + i][1];
7322
7323       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7324           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7325       {
7326         new_move_dir = move_dir;
7327
7328         break;
7329       }
7330
7331       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7332         continue;
7333
7334       move_dir_preference = -1 * RunnerVisit[xx][yy];
7335       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7336         move_dir_preference = PlayerVisit[xx][yy];
7337
7338       if (move_dir_preference > move_preference)
7339       {
7340         // prefer field that has not been visited for the longest time
7341         move_preference = move_dir_preference;
7342         new_move_dir = move_dir;
7343       }
7344       else if (move_dir_preference == move_preference &&
7345                move_dir == old_move_dir)
7346       {
7347         // prefer last direction when all directions are preferred equally
7348         move_preference = move_dir_preference;
7349         new_move_dir = move_dir;
7350       }
7351     }
7352
7353     MovDir[x][y] = new_move_dir;
7354     if (old_move_dir != new_move_dir)
7355       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7356   }
7357 }
7358
7359 static void TurnRound(int x, int y)
7360 {
7361   int direction = MovDir[x][y];
7362
7363   TurnRoundExt(x, y);
7364
7365   GfxDir[x][y] = MovDir[x][y];
7366
7367   if (direction != MovDir[x][y])
7368     GfxFrame[x][y] = 0;
7369
7370   if (MovDelay[x][y])
7371     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7372
7373   ResetGfxFrame(x, y);
7374 }
7375
7376 static boolean JustBeingPushed(int x, int y)
7377 {
7378   int i;
7379
7380   for (i = 0; i < MAX_PLAYERS; i++)
7381   {
7382     struct PlayerInfo *player = &stored_player[i];
7383
7384     if (player->active && player->is_pushing && player->MovPos)
7385     {
7386       int next_jx = player->jx + (player->jx - player->last_jx);
7387       int next_jy = player->jy + (player->jy - player->last_jy);
7388
7389       if (x == next_jx && y == next_jy)
7390         return TRUE;
7391     }
7392   }
7393
7394   return FALSE;
7395 }
7396
7397 static void StartMoving(int x, int y)
7398 {
7399   boolean started_moving = FALSE;       // some elements can fall _and_ move
7400   int element = Feld[x][y];
7401
7402   if (Stop[x][y])
7403     return;
7404
7405   if (MovDelay[x][y] == 0)
7406     GfxAction[x][y] = ACTION_DEFAULT;
7407
7408   if (CAN_FALL(element) && y < lev_fieldy - 1)
7409   {
7410     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7411         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7412       if (JustBeingPushed(x, y))
7413         return;
7414
7415     if (element == EL_QUICKSAND_FULL)
7416     {
7417       if (IS_FREE(x, y + 1))
7418       {
7419         InitMovingField(x, y, MV_DOWN);
7420         started_moving = TRUE;
7421
7422         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7423 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7424         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7425           Store[x][y] = EL_ROCK;
7426 #else
7427         Store[x][y] = EL_ROCK;
7428 #endif
7429
7430         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7431       }
7432       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7433       {
7434         if (!MovDelay[x][y])
7435         {
7436           MovDelay[x][y] = TILEY + 1;
7437
7438           ResetGfxAnimation(x, y);
7439           ResetGfxAnimation(x, y + 1);
7440         }
7441
7442         if (MovDelay[x][y])
7443         {
7444           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7445           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7446
7447           MovDelay[x][y]--;
7448           if (MovDelay[x][y])
7449             return;
7450         }
7451
7452         Feld[x][y] = EL_QUICKSAND_EMPTY;
7453         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7454         Store[x][y + 1] = Store[x][y];
7455         Store[x][y] = 0;
7456
7457         PlayLevelSoundAction(x, y, ACTION_FILLING);
7458       }
7459       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7460       {
7461         if (!MovDelay[x][y])
7462         {
7463           MovDelay[x][y] = TILEY + 1;
7464
7465           ResetGfxAnimation(x, y);
7466           ResetGfxAnimation(x, y + 1);
7467         }
7468
7469         if (MovDelay[x][y])
7470         {
7471           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7472           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7473
7474           MovDelay[x][y]--;
7475           if (MovDelay[x][y])
7476             return;
7477         }
7478
7479         Feld[x][y] = EL_QUICKSAND_EMPTY;
7480         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7481         Store[x][y + 1] = Store[x][y];
7482         Store[x][y] = 0;
7483
7484         PlayLevelSoundAction(x, y, ACTION_FILLING);
7485       }
7486     }
7487     else if (element == EL_QUICKSAND_FAST_FULL)
7488     {
7489       if (IS_FREE(x, y + 1))
7490       {
7491         InitMovingField(x, y, MV_DOWN);
7492         started_moving = TRUE;
7493
7494         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7495 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7496         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7497           Store[x][y] = EL_ROCK;
7498 #else
7499         Store[x][y] = EL_ROCK;
7500 #endif
7501
7502         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7503       }
7504       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7505       {
7506         if (!MovDelay[x][y])
7507         {
7508           MovDelay[x][y] = TILEY + 1;
7509
7510           ResetGfxAnimation(x, y);
7511           ResetGfxAnimation(x, y + 1);
7512         }
7513
7514         if (MovDelay[x][y])
7515         {
7516           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7517           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7518
7519           MovDelay[x][y]--;
7520           if (MovDelay[x][y])
7521             return;
7522         }
7523
7524         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7525         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7526         Store[x][y + 1] = Store[x][y];
7527         Store[x][y] = 0;
7528
7529         PlayLevelSoundAction(x, y, ACTION_FILLING);
7530       }
7531       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7532       {
7533         if (!MovDelay[x][y])
7534         {
7535           MovDelay[x][y] = TILEY + 1;
7536
7537           ResetGfxAnimation(x, y);
7538           ResetGfxAnimation(x, y + 1);
7539         }
7540
7541         if (MovDelay[x][y])
7542         {
7543           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7544           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7545
7546           MovDelay[x][y]--;
7547           if (MovDelay[x][y])
7548             return;
7549         }
7550
7551         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7552         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7553         Store[x][y + 1] = Store[x][y];
7554         Store[x][y] = 0;
7555
7556         PlayLevelSoundAction(x, y, ACTION_FILLING);
7557       }
7558     }
7559     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7560              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7561     {
7562       InitMovingField(x, y, MV_DOWN);
7563       started_moving = TRUE;
7564
7565       Feld[x][y] = EL_QUICKSAND_FILLING;
7566       Store[x][y] = element;
7567
7568       PlayLevelSoundAction(x, y, ACTION_FILLING);
7569     }
7570     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7571              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7572     {
7573       InitMovingField(x, y, MV_DOWN);
7574       started_moving = TRUE;
7575
7576       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7577       Store[x][y] = element;
7578
7579       PlayLevelSoundAction(x, y, ACTION_FILLING);
7580     }
7581     else if (element == EL_MAGIC_WALL_FULL)
7582     {
7583       if (IS_FREE(x, y + 1))
7584       {
7585         InitMovingField(x, y, MV_DOWN);
7586         started_moving = TRUE;
7587
7588         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7589         Store[x][y] = EL_CHANGED(Store[x][y]);
7590       }
7591       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7592       {
7593         if (!MovDelay[x][y])
7594           MovDelay[x][y] = TILEY / 4 + 1;
7595
7596         if (MovDelay[x][y])
7597         {
7598           MovDelay[x][y]--;
7599           if (MovDelay[x][y])
7600             return;
7601         }
7602
7603         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7604         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7605         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7606         Store[x][y] = 0;
7607       }
7608     }
7609     else if (element == EL_BD_MAGIC_WALL_FULL)
7610     {
7611       if (IS_FREE(x, y + 1))
7612       {
7613         InitMovingField(x, y, MV_DOWN);
7614         started_moving = TRUE;
7615
7616         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7617         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7618       }
7619       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7620       {
7621         if (!MovDelay[x][y])
7622           MovDelay[x][y] = TILEY / 4 + 1;
7623
7624         if (MovDelay[x][y])
7625         {
7626           MovDelay[x][y]--;
7627           if (MovDelay[x][y])
7628             return;
7629         }
7630
7631         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7632         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7633         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7634         Store[x][y] = 0;
7635       }
7636     }
7637     else if (element == EL_DC_MAGIC_WALL_FULL)
7638     {
7639       if (IS_FREE(x, y + 1))
7640       {
7641         InitMovingField(x, y, MV_DOWN);
7642         started_moving = TRUE;
7643
7644         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7645         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7646       }
7647       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7648       {
7649         if (!MovDelay[x][y])
7650           MovDelay[x][y] = TILEY / 4 + 1;
7651
7652         if (MovDelay[x][y])
7653         {
7654           MovDelay[x][y]--;
7655           if (MovDelay[x][y])
7656             return;
7657         }
7658
7659         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7660         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7661         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7662         Store[x][y] = 0;
7663       }
7664     }
7665     else if ((CAN_PASS_MAGIC_WALL(element) &&
7666               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7667                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7668              (CAN_PASS_DC_MAGIC_WALL(element) &&
7669               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7670
7671     {
7672       InitMovingField(x, y, MV_DOWN);
7673       started_moving = TRUE;
7674
7675       Feld[x][y] =
7676         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7677          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7678          EL_DC_MAGIC_WALL_FILLING);
7679       Store[x][y] = element;
7680     }
7681     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7682     {
7683       SplashAcid(x, y + 1);
7684
7685       InitMovingField(x, y, MV_DOWN);
7686       started_moving = TRUE;
7687
7688       Store[x][y] = EL_ACID;
7689     }
7690     else if (
7691              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7692               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7693              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7694               CAN_FALL(element) && WasJustFalling[x][y] &&
7695               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7696
7697              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7698               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7699               (Feld[x][y + 1] == EL_BLOCKED)))
7700     {
7701       /* this is needed for a special case not covered by calling "Impact()"
7702          from "ContinueMoving()": if an element moves to a tile directly below
7703          another element which was just falling on that tile (which was empty
7704          in the previous frame), the falling element above would just stop
7705          instead of smashing the element below (in previous version, the above
7706          element was just checked for "moving" instead of "falling", resulting
7707          in incorrect smashes caused by horizontal movement of the above
7708          element; also, the case of the player being the element to smash was
7709          simply not covered here... :-/ ) */
7710
7711       CheckCollision[x][y] = 0;
7712       CheckImpact[x][y] = 0;
7713
7714       Impact(x, y);
7715     }
7716     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7717     {
7718       if (MovDir[x][y] == MV_NONE)
7719       {
7720         InitMovingField(x, y, MV_DOWN);
7721         started_moving = TRUE;
7722       }
7723     }
7724     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7725     {
7726       if (WasJustFalling[x][y]) // prevent animation from being restarted
7727         MovDir[x][y] = MV_DOWN;
7728
7729       InitMovingField(x, y, MV_DOWN);
7730       started_moving = TRUE;
7731     }
7732     else if (element == EL_AMOEBA_DROP)
7733     {
7734       Feld[x][y] = EL_AMOEBA_GROWING;
7735       Store[x][y] = EL_AMOEBA_WET;
7736     }
7737     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7738               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7739              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7740              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7741     {
7742       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7743                                 (IS_FREE(x - 1, y + 1) ||
7744                                  Feld[x - 1][y + 1] == EL_ACID));
7745       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7746                                 (IS_FREE(x + 1, y + 1) ||
7747                                  Feld[x + 1][y + 1] == EL_ACID));
7748       boolean can_fall_any  = (can_fall_left || can_fall_right);
7749       boolean can_fall_both = (can_fall_left && can_fall_right);
7750       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7751
7752       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7753       {
7754         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7755           can_fall_right = FALSE;
7756         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7757           can_fall_left = FALSE;
7758         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7759           can_fall_right = FALSE;
7760         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7761           can_fall_left = FALSE;
7762
7763         can_fall_any  = (can_fall_left || can_fall_right);
7764         can_fall_both = FALSE;
7765       }
7766
7767       if (can_fall_both)
7768       {
7769         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7770           can_fall_right = FALSE;       // slip down on left side
7771         else
7772           can_fall_left = !(can_fall_right = RND(2));
7773
7774         can_fall_both = FALSE;
7775       }
7776
7777       if (can_fall_any)
7778       {
7779         // if not determined otherwise, prefer left side for slipping down
7780         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7781         started_moving = TRUE;
7782       }
7783     }
7784     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7785     {
7786       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7787       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7788       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7789       int belt_dir = game.belt_dir[belt_nr];
7790
7791       if ((belt_dir == MV_LEFT  && left_is_free) ||
7792           (belt_dir == MV_RIGHT && right_is_free))
7793       {
7794         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7795
7796         InitMovingField(x, y, belt_dir);
7797         started_moving = TRUE;
7798
7799         Pushed[x][y] = TRUE;
7800         Pushed[nextx][y] = TRUE;
7801
7802         GfxAction[x][y] = ACTION_DEFAULT;
7803       }
7804       else
7805       {
7806         MovDir[x][y] = 0;       // if element was moving, stop it
7807       }
7808     }
7809   }
7810
7811   // not "else if" because of elements that can fall and move (EL_SPRING)
7812   if (CAN_MOVE(element) && !started_moving)
7813   {
7814     int move_pattern = element_info[element].move_pattern;
7815     int newx, newy;
7816
7817     Moving2Blocked(x, y, &newx, &newy);
7818
7819     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7820       return;
7821
7822     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7823         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7824     {
7825       WasJustMoving[x][y] = 0;
7826       CheckCollision[x][y] = 0;
7827
7828       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7829
7830       if (Feld[x][y] != element)        // element has changed
7831         return;
7832     }
7833
7834     if (!MovDelay[x][y])        // start new movement phase
7835     {
7836       // all objects that can change their move direction after each step
7837       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7838
7839       if (element != EL_YAMYAM &&
7840           element != EL_DARK_YAMYAM &&
7841           element != EL_PACMAN &&
7842           !(move_pattern & MV_ANY_DIRECTION) &&
7843           move_pattern != MV_TURNING_LEFT &&
7844           move_pattern != MV_TURNING_RIGHT &&
7845           move_pattern != MV_TURNING_LEFT_RIGHT &&
7846           move_pattern != MV_TURNING_RIGHT_LEFT &&
7847           move_pattern != MV_TURNING_RANDOM)
7848       {
7849         TurnRound(x, y);
7850
7851         if (MovDelay[x][y] && (element == EL_BUG ||
7852                                element == EL_SPACESHIP ||
7853                                element == EL_SP_SNIKSNAK ||
7854                                element == EL_SP_ELECTRON ||
7855                                element == EL_MOLE))
7856           TEST_DrawLevelField(x, y);
7857       }
7858     }
7859
7860     if (MovDelay[x][y])         // wait some time before next movement
7861     {
7862       MovDelay[x][y]--;
7863
7864       if (element == EL_ROBOT ||
7865           element == EL_YAMYAM ||
7866           element == EL_DARK_YAMYAM)
7867       {
7868         DrawLevelElementAnimationIfNeeded(x, y, element);
7869         PlayLevelSoundAction(x, y, ACTION_WAITING);
7870       }
7871       else if (element == EL_SP_ELECTRON)
7872         DrawLevelElementAnimationIfNeeded(x, y, element);
7873       else if (element == EL_DRAGON)
7874       {
7875         int i;
7876         int dir = MovDir[x][y];
7877         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7878         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7879         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7880                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7881                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7882                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7883         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7884
7885         GfxAction[x][y] = ACTION_ATTACKING;
7886
7887         if (IS_PLAYER(x, y))
7888           DrawPlayerField(x, y);
7889         else
7890           TEST_DrawLevelField(x, y);
7891
7892         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7893
7894         for (i = 1; i <= 3; i++)
7895         {
7896           int xx = x + i * dx;
7897           int yy = y + i * dy;
7898           int sx = SCREENX(xx);
7899           int sy = SCREENY(yy);
7900           int flame_graphic = graphic + (i - 1);
7901
7902           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7903             break;
7904
7905           if (MovDelay[x][y])
7906           {
7907             int flamed = MovingOrBlocked2Element(xx, yy);
7908
7909             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7910               Bang(xx, yy);
7911             else
7912               RemoveMovingField(xx, yy);
7913
7914             ChangeDelay[xx][yy] = 0;
7915
7916             Feld[xx][yy] = EL_FLAMES;
7917
7918             if (IN_SCR_FIELD(sx, sy))
7919             {
7920               TEST_DrawLevelFieldCrumbled(xx, yy);
7921               DrawGraphic(sx, sy, flame_graphic, frame);
7922             }
7923           }
7924           else
7925           {
7926             if (Feld[xx][yy] == EL_FLAMES)
7927               Feld[xx][yy] = EL_EMPTY;
7928             TEST_DrawLevelField(xx, yy);
7929           }
7930         }
7931       }
7932
7933       if (MovDelay[x][y])       // element still has to wait some time
7934       {
7935         PlayLevelSoundAction(x, y, ACTION_WAITING);
7936
7937         return;
7938       }
7939     }
7940
7941     // now make next step
7942
7943     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7944
7945     if (DONT_COLLIDE_WITH(element) &&
7946         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7947         !PLAYER_ENEMY_PROTECTED(newx, newy))
7948     {
7949       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7950
7951       return;
7952     }
7953
7954     else if (CAN_MOVE_INTO_ACID(element) &&
7955              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7956              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7957              (MovDir[x][y] == MV_DOWN ||
7958               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7959     {
7960       SplashAcid(newx, newy);
7961       Store[x][y] = EL_ACID;
7962     }
7963     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7964     {
7965       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7966           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7967           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7968           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7969       {
7970         RemoveField(x, y);
7971         TEST_DrawLevelField(x, y);
7972
7973         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7974         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7975           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7976
7977         game.friends_still_needed--;
7978         if (!game.friends_still_needed &&
7979             !game.GameOver &&
7980             game.all_players_gone)
7981           LevelSolved();
7982
7983         return;
7984       }
7985       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7986       {
7987         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7988           TEST_DrawLevelField(newx, newy);
7989         else
7990           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7991       }
7992       else if (!IS_FREE(newx, newy))
7993       {
7994         GfxAction[x][y] = ACTION_WAITING;
7995
7996         if (IS_PLAYER(x, y))
7997           DrawPlayerField(x, y);
7998         else
7999           TEST_DrawLevelField(x, y);
8000
8001         return;
8002       }
8003     }
8004     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8005     {
8006       if (IS_FOOD_PIG(Feld[newx][newy]))
8007       {
8008         if (IS_MOVING(newx, newy))
8009           RemoveMovingField(newx, newy);
8010         else
8011         {
8012           Feld[newx][newy] = EL_EMPTY;
8013           TEST_DrawLevelField(newx, newy);
8014         }
8015
8016         PlayLevelSound(x, y, SND_PIG_DIGGING);
8017       }
8018       else if (!IS_FREE(newx, newy))
8019       {
8020         if (IS_PLAYER(x, y))
8021           DrawPlayerField(x, y);
8022         else
8023           TEST_DrawLevelField(x, y);
8024
8025         return;
8026       }
8027     }
8028     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8029     {
8030       if (Store[x][y] != EL_EMPTY)
8031       {
8032         boolean can_clone = FALSE;
8033         int xx, yy;
8034
8035         // check if element to clone is still there
8036         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8037         {
8038           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8039           {
8040             can_clone = TRUE;
8041
8042             break;
8043           }
8044         }
8045
8046         // cannot clone or target field not free anymore -- do not clone
8047         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8048           Store[x][y] = EL_EMPTY;
8049       }
8050
8051       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8052       {
8053         if (IS_MV_DIAGONAL(MovDir[x][y]))
8054         {
8055           int diagonal_move_dir = MovDir[x][y];
8056           int stored = Store[x][y];
8057           int change_delay = 8;
8058           int graphic;
8059
8060           // android is moving diagonally
8061
8062           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8063
8064           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8065           GfxElement[x][y] = EL_EMC_ANDROID;
8066           GfxAction[x][y] = ACTION_SHRINKING;
8067           GfxDir[x][y] = diagonal_move_dir;
8068           ChangeDelay[x][y] = change_delay;
8069
8070           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8071                                    GfxDir[x][y]);
8072
8073           DrawLevelGraphicAnimation(x, y, graphic);
8074           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8075
8076           if (Feld[newx][newy] == EL_ACID)
8077           {
8078             SplashAcid(newx, newy);
8079
8080             return;
8081           }
8082
8083           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8084
8085           Store[newx][newy] = EL_EMC_ANDROID;
8086           GfxElement[newx][newy] = EL_EMC_ANDROID;
8087           GfxAction[newx][newy] = ACTION_GROWING;
8088           GfxDir[newx][newy] = diagonal_move_dir;
8089           ChangeDelay[newx][newy] = change_delay;
8090
8091           graphic = el_act_dir2img(GfxElement[newx][newy],
8092                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8093
8094           DrawLevelGraphicAnimation(newx, newy, graphic);
8095           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8096
8097           return;
8098         }
8099         else
8100         {
8101           Feld[newx][newy] = EL_EMPTY;
8102           TEST_DrawLevelField(newx, newy);
8103
8104           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8105         }
8106       }
8107       else if (!IS_FREE(newx, newy))
8108       {
8109         return;
8110       }
8111     }
8112     else if (IS_CUSTOM_ELEMENT(element) &&
8113              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8114     {
8115       if (!DigFieldByCE(newx, newy, element))
8116         return;
8117
8118       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8119       {
8120         RunnerVisit[x][y] = FrameCounter;
8121         PlayerVisit[x][y] /= 8;         // expire player visit path
8122       }
8123     }
8124     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8125     {
8126       if (!IS_FREE(newx, newy))
8127       {
8128         if (IS_PLAYER(x, y))
8129           DrawPlayerField(x, y);
8130         else
8131           TEST_DrawLevelField(x, y);
8132
8133         return;
8134       }
8135       else
8136       {
8137         boolean wanna_flame = !RND(10);
8138         int dx = newx - x, dy = newy - y;
8139         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8140         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8141         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8142                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8143         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8144                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8145
8146         if ((wanna_flame ||
8147              IS_CLASSIC_ENEMY(element1) ||
8148              IS_CLASSIC_ENEMY(element2)) &&
8149             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8150             element1 != EL_FLAMES && element2 != EL_FLAMES)
8151         {
8152           ResetGfxAnimation(x, y);
8153           GfxAction[x][y] = ACTION_ATTACKING;
8154
8155           if (IS_PLAYER(x, y))
8156             DrawPlayerField(x, y);
8157           else
8158             TEST_DrawLevelField(x, y);
8159
8160           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8161
8162           MovDelay[x][y] = 50;
8163
8164           Feld[newx][newy] = EL_FLAMES;
8165           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8166             Feld[newx1][newy1] = EL_FLAMES;
8167           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8168             Feld[newx2][newy2] = EL_FLAMES;
8169
8170           return;
8171         }
8172       }
8173     }
8174     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8175              Feld[newx][newy] == EL_DIAMOND)
8176     {
8177       if (IS_MOVING(newx, newy))
8178         RemoveMovingField(newx, newy);
8179       else
8180       {
8181         Feld[newx][newy] = EL_EMPTY;
8182         TEST_DrawLevelField(newx, newy);
8183       }
8184
8185       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8186     }
8187     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8188              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8189     {
8190       if (AmoebaNr[newx][newy])
8191       {
8192         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8193         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8194             Feld[newx][newy] == EL_BD_AMOEBA)
8195           AmoebaCnt[AmoebaNr[newx][newy]]--;
8196       }
8197
8198       if (IS_MOVING(newx, newy))
8199       {
8200         RemoveMovingField(newx, newy);
8201       }
8202       else
8203       {
8204         Feld[newx][newy] = EL_EMPTY;
8205         TEST_DrawLevelField(newx, newy);
8206       }
8207
8208       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8209     }
8210     else if ((element == EL_PACMAN || element == EL_MOLE)
8211              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8212     {
8213       if (AmoebaNr[newx][newy])
8214       {
8215         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8216         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8217             Feld[newx][newy] == EL_BD_AMOEBA)
8218           AmoebaCnt[AmoebaNr[newx][newy]]--;
8219       }
8220
8221       if (element == EL_MOLE)
8222       {
8223         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8224         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8225
8226         ResetGfxAnimation(x, y);
8227         GfxAction[x][y] = ACTION_DIGGING;
8228         TEST_DrawLevelField(x, y);
8229
8230         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8231
8232         return;                         // wait for shrinking amoeba
8233       }
8234       else      // element == EL_PACMAN
8235       {
8236         Feld[newx][newy] = EL_EMPTY;
8237         TEST_DrawLevelField(newx, newy);
8238         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8239       }
8240     }
8241     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8242              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8243               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8244     {
8245       // wait for shrinking amoeba to completely disappear
8246       return;
8247     }
8248     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8249     {
8250       // object was running against a wall
8251
8252       TurnRound(x, y);
8253
8254       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8255         DrawLevelElementAnimation(x, y, element);
8256
8257       if (DONT_TOUCH(element))
8258         TestIfBadThingTouchesPlayer(x, y);
8259
8260       return;
8261     }
8262
8263     InitMovingField(x, y, MovDir[x][y]);
8264
8265     PlayLevelSoundAction(x, y, ACTION_MOVING);
8266   }
8267
8268   if (MovDir[x][y])
8269     ContinueMoving(x, y);
8270 }
8271
8272 void ContinueMoving(int x, int y)
8273 {
8274   int element = Feld[x][y];
8275   struct ElementInfo *ei = &element_info[element];
8276   int direction = MovDir[x][y];
8277   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8278   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8279   int newx = x + dx, newy = y + dy;
8280   int stored = Store[x][y];
8281   int stored_new = Store[newx][newy];
8282   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8283   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8284   boolean last_line = (newy == lev_fieldy - 1);
8285
8286   MovPos[x][y] += getElementMoveStepsize(x, y);
8287
8288   if (pushed_by_player) // special case: moving object pushed by player
8289     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8290
8291   if (ABS(MovPos[x][y]) < TILEX)
8292   {
8293     TEST_DrawLevelField(x, y);
8294
8295     return;     // element is still moving
8296   }
8297
8298   // element reached destination field
8299
8300   Feld[x][y] = EL_EMPTY;
8301   Feld[newx][newy] = element;
8302   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8303
8304   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8305   {
8306     element = Feld[newx][newy] = EL_ACID;
8307   }
8308   else if (element == EL_MOLE)
8309   {
8310     Feld[x][y] = EL_SAND;
8311
8312     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8313   }
8314   else if (element == EL_QUICKSAND_FILLING)
8315   {
8316     element = Feld[newx][newy] = get_next_element(element);
8317     Store[newx][newy] = Store[x][y];
8318   }
8319   else if (element == EL_QUICKSAND_EMPTYING)
8320   {
8321     Feld[x][y] = get_next_element(element);
8322     element = Feld[newx][newy] = Store[x][y];
8323   }
8324   else if (element == EL_QUICKSAND_FAST_FILLING)
8325   {
8326     element = Feld[newx][newy] = get_next_element(element);
8327     Store[newx][newy] = Store[x][y];
8328   }
8329   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8330   {
8331     Feld[x][y] = get_next_element(element);
8332     element = Feld[newx][newy] = Store[x][y];
8333   }
8334   else if (element == EL_MAGIC_WALL_FILLING)
8335   {
8336     element = Feld[newx][newy] = get_next_element(element);
8337     if (!game.magic_wall_active)
8338       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8339     Store[newx][newy] = Store[x][y];
8340   }
8341   else if (element == EL_MAGIC_WALL_EMPTYING)
8342   {
8343     Feld[x][y] = get_next_element(element);
8344     if (!game.magic_wall_active)
8345       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8346     element = Feld[newx][newy] = Store[x][y];
8347
8348     InitField(newx, newy, FALSE);
8349   }
8350   else if (element == EL_BD_MAGIC_WALL_FILLING)
8351   {
8352     element = Feld[newx][newy] = get_next_element(element);
8353     if (!game.magic_wall_active)
8354       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8355     Store[newx][newy] = Store[x][y];
8356   }
8357   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8358   {
8359     Feld[x][y] = get_next_element(element);
8360     if (!game.magic_wall_active)
8361       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8362     element = Feld[newx][newy] = Store[x][y];
8363
8364     InitField(newx, newy, FALSE);
8365   }
8366   else if (element == EL_DC_MAGIC_WALL_FILLING)
8367   {
8368     element = Feld[newx][newy] = get_next_element(element);
8369     if (!game.magic_wall_active)
8370       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8371     Store[newx][newy] = Store[x][y];
8372   }
8373   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8374   {
8375     Feld[x][y] = get_next_element(element);
8376     if (!game.magic_wall_active)
8377       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8378     element = Feld[newx][newy] = Store[x][y];
8379
8380     InitField(newx, newy, FALSE);
8381   }
8382   else if (element == EL_AMOEBA_DROPPING)
8383   {
8384     Feld[x][y] = get_next_element(element);
8385     element = Feld[newx][newy] = Store[x][y];
8386   }
8387   else if (element == EL_SOKOBAN_OBJECT)
8388   {
8389     if (Back[x][y])
8390       Feld[x][y] = Back[x][y];
8391
8392     if (Back[newx][newy])
8393       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8394
8395     Back[x][y] = Back[newx][newy] = 0;
8396   }
8397
8398   Store[x][y] = EL_EMPTY;
8399   MovPos[x][y] = 0;
8400   MovDir[x][y] = 0;
8401   MovDelay[x][y] = 0;
8402
8403   MovDelay[newx][newy] = 0;
8404
8405   if (CAN_CHANGE_OR_HAS_ACTION(element))
8406   {
8407     // copy element change control values to new field
8408     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8409     ChangePage[newx][newy]  = ChangePage[x][y];
8410     ChangeCount[newx][newy] = ChangeCount[x][y];
8411     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8412   }
8413
8414   CustomValue[newx][newy] = CustomValue[x][y];
8415
8416   ChangeDelay[x][y] = 0;
8417   ChangePage[x][y] = -1;
8418   ChangeCount[x][y] = 0;
8419   ChangeEvent[x][y] = -1;
8420
8421   CustomValue[x][y] = 0;
8422
8423   // copy animation control values to new field
8424   GfxFrame[newx][newy]  = GfxFrame[x][y];
8425   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8426   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8427   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8428
8429   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8430
8431   // some elements can leave other elements behind after moving
8432   if (ei->move_leave_element != EL_EMPTY &&
8433       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8434       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8435   {
8436     int move_leave_element = ei->move_leave_element;
8437
8438     // this makes it possible to leave the removed element again
8439     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8440       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8441
8442     Feld[x][y] = move_leave_element;
8443
8444     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8445       MovDir[x][y] = direction;
8446
8447     InitField(x, y, FALSE);
8448
8449     if (GFX_CRUMBLED(Feld[x][y]))
8450       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8451
8452     if (ELEM_IS_PLAYER(move_leave_element))
8453       RelocatePlayer(x, y, move_leave_element);
8454   }
8455
8456   // do this after checking for left-behind element
8457   ResetGfxAnimation(x, y);      // reset animation values for old field
8458
8459   if (!CAN_MOVE(element) ||
8460       (CAN_FALL(element) && direction == MV_DOWN &&
8461        (element == EL_SPRING ||
8462         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8463         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8464     GfxDir[x][y] = MovDir[newx][newy] = 0;
8465
8466   TEST_DrawLevelField(x, y);
8467   TEST_DrawLevelField(newx, newy);
8468
8469   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8470
8471   // prevent pushed element from moving on in pushed direction
8472   if (pushed_by_player && CAN_MOVE(element) &&
8473       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8474       !(element_info[element].move_pattern & direction))
8475     TurnRound(newx, newy);
8476
8477   // prevent elements on conveyor belt from moving on in last direction
8478   if (pushed_by_conveyor && CAN_FALL(element) &&
8479       direction & MV_HORIZONTAL)
8480     MovDir[newx][newy] = 0;
8481
8482   if (!pushed_by_player)
8483   {
8484     int nextx = newx + dx, nexty = newy + dy;
8485     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8486
8487     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8488
8489     if (CAN_FALL(element) && direction == MV_DOWN)
8490       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8491
8492     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8493       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8494
8495     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8496       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8497   }
8498
8499   if (DONT_TOUCH(element))      // object may be nasty to player or others
8500   {
8501     TestIfBadThingTouchesPlayer(newx, newy);
8502     TestIfBadThingTouchesFriend(newx, newy);
8503
8504     if (!IS_CUSTOM_ELEMENT(element))
8505       TestIfBadThingTouchesOtherBadThing(newx, newy);
8506   }
8507   else if (element == EL_PENGUIN)
8508     TestIfFriendTouchesBadThing(newx, newy);
8509
8510   if (DONT_GET_HIT_BY(element))
8511   {
8512     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8513   }
8514
8515   // give the player one last chance (one more frame) to move away
8516   if (CAN_FALL(element) && direction == MV_DOWN &&
8517       (last_line || (!IS_FREE(x, newy + 1) &&
8518                      (!IS_PLAYER(x, newy + 1) ||
8519                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8520     Impact(x, newy);
8521
8522   if (pushed_by_player && !game.use_change_when_pushing_bug)
8523   {
8524     int push_side = MV_DIR_OPPOSITE(direction);
8525     struct PlayerInfo *player = PLAYERINFO(x, y);
8526
8527     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8528                                player->index_bit, push_side);
8529     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8530                                         player->index_bit, push_side);
8531   }
8532
8533   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8534     MovDelay[newx][newy] = 1;
8535
8536   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8537
8538   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8539   TestIfElementHitsCustomElement(newx, newy, direction);
8540   TestIfPlayerTouchesCustomElement(newx, newy);
8541   TestIfElementTouchesCustomElement(newx, newy);
8542
8543   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8544       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8545     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8546                              MV_DIR_OPPOSITE(direction));
8547 }
8548
8549 int AmoebeNachbarNr(int ax, int ay)
8550 {
8551   int i;
8552   int element = Feld[ax][ay];
8553   int group_nr = 0;
8554   static int xy[4][2] =
8555   {
8556     { 0, -1 },
8557     { -1, 0 },
8558     { +1, 0 },
8559     { 0, +1 }
8560   };
8561
8562   for (i = 0; i < NUM_DIRECTIONS; i++)
8563   {
8564     int x = ax + xy[i][0];
8565     int y = ay + xy[i][1];
8566
8567     if (!IN_LEV_FIELD(x, y))
8568       continue;
8569
8570     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8571       group_nr = AmoebaNr[x][y];
8572   }
8573
8574   return group_nr;
8575 }
8576
8577 static void AmoebenVereinigen(int ax, int ay)
8578 {
8579   int i, x, y, xx, yy;
8580   int new_group_nr = AmoebaNr[ax][ay];
8581   static int xy[4][2] =
8582   {
8583     { 0, -1 },
8584     { -1, 0 },
8585     { +1, 0 },
8586     { 0, +1 }
8587   };
8588
8589   if (new_group_nr == 0)
8590     return;
8591
8592   for (i = 0; i < NUM_DIRECTIONS; i++)
8593   {
8594     x = ax + xy[i][0];
8595     y = ay + xy[i][1];
8596
8597     if (!IN_LEV_FIELD(x, y))
8598       continue;
8599
8600     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8601          Feld[x][y] == EL_BD_AMOEBA ||
8602          Feld[x][y] == EL_AMOEBA_DEAD) &&
8603         AmoebaNr[x][y] != new_group_nr)
8604     {
8605       int old_group_nr = AmoebaNr[x][y];
8606
8607       if (old_group_nr == 0)
8608         return;
8609
8610       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8611       AmoebaCnt[old_group_nr] = 0;
8612       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8613       AmoebaCnt2[old_group_nr] = 0;
8614
8615       SCAN_PLAYFIELD(xx, yy)
8616       {
8617         if (AmoebaNr[xx][yy] == old_group_nr)
8618           AmoebaNr[xx][yy] = new_group_nr;
8619       }
8620     }
8621   }
8622 }
8623
8624 void AmoebeUmwandeln(int ax, int ay)
8625 {
8626   int i, x, y;
8627
8628   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8629   {
8630     int group_nr = AmoebaNr[ax][ay];
8631
8632 #ifdef DEBUG
8633     if (group_nr == 0)
8634     {
8635       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8636       printf("AmoebeUmwandeln(): This should never happen!\n");
8637       return;
8638     }
8639 #endif
8640
8641     SCAN_PLAYFIELD(x, y)
8642     {
8643       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8644       {
8645         AmoebaNr[x][y] = 0;
8646         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8647       }
8648     }
8649
8650     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8651                             SND_AMOEBA_TURNING_TO_GEM :
8652                             SND_AMOEBA_TURNING_TO_ROCK));
8653     Bang(ax, ay);
8654   }
8655   else
8656   {
8657     static int xy[4][2] =
8658     {
8659       { 0, -1 },
8660       { -1, 0 },
8661       { +1, 0 },
8662       { 0, +1 }
8663     };
8664
8665     for (i = 0; i < NUM_DIRECTIONS; i++)
8666     {
8667       x = ax + xy[i][0];
8668       y = ay + xy[i][1];
8669
8670       if (!IN_LEV_FIELD(x, y))
8671         continue;
8672
8673       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8674       {
8675         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8676                               SND_AMOEBA_TURNING_TO_GEM :
8677                               SND_AMOEBA_TURNING_TO_ROCK));
8678         Bang(x, y);
8679       }
8680     }
8681   }
8682 }
8683
8684 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8685 {
8686   int x, y;
8687   int group_nr = AmoebaNr[ax][ay];
8688   boolean done = FALSE;
8689
8690 #ifdef DEBUG
8691   if (group_nr == 0)
8692   {
8693     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8694     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8695     return;
8696   }
8697 #endif
8698
8699   SCAN_PLAYFIELD(x, y)
8700   {
8701     if (AmoebaNr[x][y] == group_nr &&
8702         (Feld[x][y] == EL_AMOEBA_DEAD ||
8703          Feld[x][y] == EL_BD_AMOEBA ||
8704          Feld[x][y] == EL_AMOEBA_GROWING))
8705     {
8706       AmoebaNr[x][y] = 0;
8707       Feld[x][y] = new_element;
8708       InitField(x, y, FALSE);
8709       TEST_DrawLevelField(x, y);
8710       done = TRUE;
8711     }
8712   }
8713
8714   if (done)
8715     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8716                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8717                             SND_BD_AMOEBA_TURNING_TO_GEM));
8718 }
8719
8720 static void AmoebeWaechst(int x, int y)
8721 {
8722   static unsigned int sound_delay = 0;
8723   static unsigned int sound_delay_value = 0;
8724
8725   if (!MovDelay[x][y])          // start new growing cycle
8726   {
8727     MovDelay[x][y] = 7;
8728
8729     if (DelayReached(&sound_delay, sound_delay_value))
8730     {
8731       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8732       sound_delay_value = 30;
8733     }
8734   }
8735
8736   if (MovDelay[x][y])           // wait some time before growing bigger
8737   {
8738     MovDelay[x][y]--;
8739     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8740     {
8741       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8742                                            6 - MovDelay[x][y]);
8743
8744       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8745     }
8746
8747     if (!MovDelay[x][y])
8748     {
8749       Feld[x][y] = Store[x][y];
8750       Store[x][y] = 0;
8751       TEST_DrawLevelField(x, y);
8752     }
8753   }
8754 }
8755
8756 static void AmoebaDisappearing(int x, int y)
8757 {
8758   static unsigned int sound_delay = 0;
8759   static unsigned int sound_delay_value = 0;
8760
8761   if (!MovDelay[x][y])          // start new shrinking cycle
8762   {
8763     MovDelay[x][y] = 7;
8764
8765     if (DelayReached(&sound_delay, sound_delay_value))
8766       sound_delay_value = 30;
8767   }
8768
8769   if (MovDelay[x][y])           // wait some time before shrinking
8770   {
8771     MovDelay[x][y]--;
8772     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8773     {
8774       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8775                                            6 - MovDelay[x][y]);
8776
8777       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8778     }
8779
8780     if (!MovDelay[x][y])
8781     {
8782       Feld[x][y] = EL_EMPTY;
8783       TEST_DrawLevelField(x, y);
8784
8785       // don't let mole enter this field in this cycle;
8786       // (give priority to objects falling to this field from above)
8787       Stop[x][y] = TRUE;
8788     }
8789   }
8790 }
8791
8792 static void AmoebeAbleger(int ax, int ay)
8793 {
8794   int i;
8795   int element = Feld[ax][ay];
8796   int graphic = el2img(element);
8797   int newax = ax, neway = ay;
8798   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8799   static int xy[4][2] =
8800   {
8801     { 0, -1 },
8802     { -1, 0 },
8803     { +1, 0 },
8804     { 0, +1 }
8805   };
8806
8807   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8808   {
8809     Feld[ax][ay] = EL_AMOEBA_DEAD;
8810     TEST_DrawLevelField(ax, ay);
8811     return;
8812   }
8813
8814   if (IS_ANIMATED(graphic))
8815     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8816
8817   if (!MovDelay[ax][ay])        // start making new amoeba field
8818     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8819
8820   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8821   {
8822     MovDelay[ax][ay]--;
8823     if (MovDelay[ax][ay])
8824       return;
8825   }
8826
8827   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8828   {
8829     int start = RND(4);
8830     int x = ax + xy[start][0];
8831     int y = ay + xy[start][1];
8832
8833     if (!IN_LEV_FIELD(x, y))
8834       return;
8835
8836     if (IS_FREE(x, y) ||
8837         CAN_GROW_INTO(Feld[x][y]) ||
8838         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8839         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8840     {
8841       newax = x;
8842       neway = y;
8843     }
8844
8845     if (newax == ax && neway == ay)
8846       return;
8847   }
8848   else                          // normal or "filled" (BD style) amoeba
8849   {
8850     int start = RND(4);
8851     boolean waiting_for_player = FALSE;
8852
8853     for (i = 0; i < NUM_DIRECTIONS; i++)
8854     {
8855       int j = (start + i) % 4;
8856       int x = ax + xy[j][0];
8857       int y = ay + xy[j][1];
8858
8859       if (!IN_LEV_FIELD(x, y))
8860         continue;
8861
8862       if (IS_FREE(x, y) ||
8863           CAN_GROW_INTO(Feld[x][y]) ||
8864           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8865           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8866       {
8867         newax = x;
8868         neway = y;
8869         break;
8870       }
8871       else if (IS_PLAYER(x, y))
8872         waiting_for_player = TRUE;
8873     }
8874
8875     if (newax == ax && neway == ay)             // amoeba cannot grow
8876     {
8877       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8878       {
8879         Feld[ax][ay] = EL_AMOEBA_DEAD;
8880         TEST_DrawLevelField(ax, ay);
8881         AmoebaCnt[AmoebaNr[ax][ay]]--;
8882
8883         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8884         {
8885           if (element == EL_AMOEBA_FULL)
8886             AmoebeUmwandeln(ax, ay);
8887           else if (element == EL_BD_AMOEBA)
8888             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8889         }
8890       }
8891       return;
8892     }
8893     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8894     {
8895       // amoeba gets larger by growing in some direction
8896
8897       int new_group_nr = AmoebaNr[ax][ay];
8898
8899 #ifdef DEBUG
8900   if (new_group_nr == 0)
8901   {
8902     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8903     printf("AmoebeAbleger(): This should never happen!\n");
8904     return;
8905   }
8906 #endif
8907
8908       AmoebaNr[newax][neway] = new_group_nr;
8909       AmoebaCnt[new_group_nr]++;
8910       AmoebaCnt2[new_group_nr]++;
8911
8912       // if amoeba touches other amoeba(s) after growing, unify them
8913       AmoebenVereinigen(newax, neway);
8914
8915       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8916       {
8917         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8918         return;
8919       }
8920     }
8921   }
8922
8923   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8924       (neway == lev_fieldy - 1 && newax != ax))
8925   {
8926     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8927     Store[newax][neway] = element;
8928   }
8929   else if (neway == ay || element == EL_EMC_DRIPPER)
8930   {
8931     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8932
8933     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8934   }
8935   else
8936   {
8937     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8938     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8939     Store[ax][ay] = EL_AMOEBA_DROP;
8940     ContinueMoving(ax, ay);
8941     return;
8942   }
8943
8944   TEST_DrawLevelField(newax, neway);
8945 }
8946
8947 static void Life(int ax, int ay)
8948 {
8949   int x1, y1, x2, y2;
8950   int life_time = 40;
8951   int element = Feld[ax][ay];
8952   int graphic = el2img(element);
8953   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8954                          level.biomaze);
8955   boolean changed = FALSE;
8956
8957   if (IS_ANIMATED(graphic))
8958     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8959
8960   if (Stop[ax][ay])
8961     return;
8962
8963   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8964     MovDelay[ax][ay] = life_time;
8965
8966   if (MovDelay[ax][ay])         // wait some time before next cycle
8967   {
8968     MovDelay[ax][ay]--;
8969     if (MovDelay[ax][ay])
8970       return;
8971   }
8972
8973   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8974   {
8975     int xx = ax+x1, yy = ay+y1;
8976     int old_element = Feld[xx][yy];
8977     int num_neighbours = 0;
8978
8979     if (!IN_LEV_FIELD(xx, yy))
8980       continue;
8981
8982     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8983     {
8984       int x = xx+x2, y = yy+y2;
8985
8986       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8987         continue;
8988
8989       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8990       boolean is_neighbour = FALSE;
8991
8992       if (level.use_life_bugs)
8993         is_neighbour =
8994           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8995            (IS_FREE(x, y)                             &&  Stop[x][y]));
8996       else
8997         is_neighbour =
8998           (Last[x][y] == element || is_player_cell);
8999
9000       if (is_neighbour)
9001         num_neighbours++;
9002     }
9003
9004     boolean is_free = FALSE;
9005
9006     if (level.use_life_bugs)
9007       is_free = (IS_FREE(xx, yy));
9008     else
9009       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9010
9011     if (xx == ax && yy == ay)           // field in the middle
9012     {
9013       if (num_neighbours < life_parameter[0] ||
9014           num_neighbours > life_parameter[1])
9015       {
9016         Feld[xx][yy] = EL_EMPTY;
9017         if (Feld[xx][yy] != old_element)
9018           TEST_DrawLevelField(xx, yy);
9019         Stop[xx][yy] = TRUE;
9020         changed = TRUE;
9021       }
9022     }
9023     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9024     {                                   // free border field
9025       if (num_neighbours >= life_parameter[2] &&
9026           num_neighbours <= life_parameter[3])
9027       {
9028         Feld[xx][yy] = element;
9029         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9030         if (Feld[xx][yy] != old_element)
9031           TEST_DrawLevelField(xx, yy);
9032         Stop[xx][yy] = TRUE;
9033         changed = TRUE;
9034       }
9035     }
9036   }
9037
9038   if (changed)
9039     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9040                    SND_GAME_OF_LIFE_GROWING);
9041 }
9042
9043 static void InitRobotWheel(int x, int y)
9044 {
9045   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9046 }
9047
9048 static void RunRobotWheel(int x, int y)
9049 {
9050   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9051 }
9052
9053 static void StopRobotWheel(int x, int y)
9054 {
9055   if (game.robot_wheel_x == x &&
9056       game.robot_wheel_y == y)
9057   {
9058     game.robot_wheel_x = -1;
9059     game.robot_wheel_y = -1;
9060     game.robot_wheel_active = FALSE;
9061   }
9062 }
9063
9064 static void InitTimegateWheel(int x, int y)
9065 {
9066   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9067 }
9068
9069 static void RunTimegateWheel(int x, int y)
9070 {
9071   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9072 }
9073
9074 static void InitMagicBallDelay(int x, int y)
9075 {
9076   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9077 }
9078
9079 static void ActivateMagicBall(int bx, int by)
9080 {
9081   int x, y;
9082
9083   if (level.ball_random)
9084   {
9085     int pos_border = RND(8);    // select one of the eight border elements
9086     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9087     int xx = pos_content % 3;
9088     int yy = pos_content / 3;
9089
9090     x = bx - 1 + xx;
9091     y = by - 1 + yy;
9092
9093     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9094       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9095   }
9096   else
9097   {
9098     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9099     {
9100       int xx = x - bx + 1;
9101       int yy = y - by + 1;
9102
9103       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9104         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9105     }
9106   }
9107
9108   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9109 }
9110
9111 static void CheckExit(int x, int y)
9112 {
9113   if (game.gems_still_needed > 0 ||
9114       game.sokoban_fields_still_needed > 0 ||
9115       game.sokoban_objects_still_needed > 0 ||
9116       game.lights_still_needed > 0)
9117   {
9118     int element = Feld[x][y];
9119     int graphic = el2img(element);
9120
9121     if (IS_ANIMATED(graphic))
9122       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9123
9124     return;
9125   }
9126
9127   // do not re-open exit door closed after last player
9128   if (game.all_players_gone)
9129     return;
9130
9131   Feld[x][y] = EL_EXIT_OPENING;
9132
9133   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9134 }
9135
9136 static void CheckExitEM(int x, int y)
9137 {
9138   if (game.gems_still_needed > 0 ||
9139       game.sokoban_fields_still_needed > 0 ||
9140       game.sokoban_objects_still_needed > 0 ||
9141       game.lights_still_needed > 0)
9142   {
9143     int element = Feld[x][y];
9144     int graphic = el2img(element);
9145
9146     if (IS_ANIMATED(graphic))
9147       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9148
9149     return;
9150   }
9151
9152   // do not re-open exit door closed after last player
9153   if (game.all_players_gone)
9154     return;
9155
9156   Feld[x][y] = EL_EM_EXIT_OPENING;
9157
9158   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9159 }
9160
9161 static void CheckExitSteel(int x, int y)
9162 {
9163   if (game.gems_still_needed > 0 ||
9164       game.sokoban_fields_still_needed > 0 ||
9165       game.sokoban_objects_still_needed > 0 ||
9166       game.lights_still_needed > 0)
9167   {
9168     int element = Feld[x][y];
9169     int graphic = el2img(element);
9170
9171     if (IS_ANIMATED(graphic))
9172       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9173
9174     return;
9175   }
9176
9177   // do not re-open exit door closed after last player
9178   if (game.all_players_gone)
9179     return;
9180
9181   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9182
9183   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9184 }
9185
9186 static void CheckExitSteelEM(int x, int y)
9187 {
9188   if (game.gems_still_needed > 0 ||
9189       game.sokoban_fields_still_needed > 0 ||
9190       game.sokoban_objects_still_needed > 0 ||
9191       game.lights_still_needed > 0)
9192   {
9193     int element = Feld[x][y];
9194     int graphic = el2img(element);
9195
9196     if (IS_ANIMATED(graphic))
9197       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9198
9199     return;
9200   }
9201
9202   // do not re-open exit door closed after last player
9203   if (game.all_players_gone)
9204     return;
9205
9206   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9207
9208   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9209 }
9210
9211 static void CheckExitSP(int x, int y)
9212 {
9213   if (game.gems_still_needed > 0)
9214   {
9215     int element = Feld[x][y];
9216     int graphic = el2img(element);
9217
9218     if (IS_ANIMATED(graphic))
9219       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9220
9221     return;
9222   }
9223
9224   // do not re-open exit door closed after last player
9225   if (game.all_players_gone)
9226     return;
9227
9228   Feld[x][y] = EL_SP_EXIT_OPENING;
9229
9230   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9231 }
9232
9233 static void CloseAllOpenTimegates(void)
9234 {
9235   int x, y;
9236
9237   SCAN_PLAYFIELD(x, y)
9238   {
9239     int element = Feld[x][y];
9240
9241     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9242     {
9243       Feld[x][y] = EL_TIMEGATE_CLOSING;
9244
9245       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9246     }
9247   }
9248 }
9249
9250 static void DrawTwinkleOnField(int x, int y)
9251 {
9252   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9253     return;
9254
9255   if (Feld[x][y] == EL_BD_DIAMOND)
9256     return;
9257
9258   if (MovDelay[x][y] == 0)      // next animation frame
9259     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9260
9261   if (MovDelay[x][y] != 0)      // wait some time before next frame
9262   {
9263     MovDelay[x][y]--;
9264
9265     DrawLevelElementAnimation(x, y, Feld[x][y]);
9266
9267     if (MovDelay[x][y] != 0)
9268     {
9269       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9270                                            10 - MovDelay[x][y]);
9271
9272       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9273     }
9274   }
9275 }
9276
9277 static void MauerWaechst(int x, int y)
9278 {
9279   int delay = 6;
9280
9281   if (!MovDelay[x][y])          // next animation frame
9282     MovDelay[x][y] = 3 * delay;
9283
9284   if (MovDelay[x][y])           // wait some time before next frame
9285   {
9286     MovDelay[x][y]--;
9287
9288     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9289     {
9290       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9291       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9292
9293       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9294     }
9295
9296     if (!MovDelay[x][y])
9297     {
9298       if (MovDir[x][y] == MV_LEFT)
9299       {
9300         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9301           TEST_DrawLevelField(x - 1, y);
9302       }
9303       else if (MovDir[x][y] == MV_RIGHT)
9304       {
9305         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9306           TEST_DrawLevelField(x + 1, y);
9307       }
9308       else if (MovDir[x][y] == MV_UP)
9309       {
9310         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9311           TEST_DrawLevelField(x, y - 1);
9312       }
9313       else
9314       {
9315         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9316           TEST_DrawLevelField(x, y + 1);
9317       }
9318
9319       Feld[x][y] = Store[x][y];
9320       Store[x][y] = 0;
9321       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9322       TEST_DrawLevelField(x, y);
9323     }
9324   }
9325 }
9326
9327 static void MauerAbleger(int ax, int ay)
9328 {
9329   int element = Feld[ax][ay];
9330   int graphic = el2img(element);
9331   boolean oben_frei = FALSE, unten_frei = FALSE;
9332   boolean links_frei = FALSE, rechts_frei = FALSE;
9333   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9334   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9335   boolean new_wall = FALSE;
9336
9337   if (IS_ANIMATED(graphic))
9338     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9339
9340   if (!MovDelay[ax][ay])        // start building new wall
9341     MovDelay[ax][ay] = 6;
9342
9343   if (MovDelay[ax][ay])         // wait some time before building new wall
9344   {
9345     MovDelay[ax][ay]--;
9346     if (MovDelay[ax][ay])
9347       return;
9348   }
9349
9350   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9351     oben_frei = TRUE;
9352   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9353     unten_frei = TRUE;
9354   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9355     links_frei = TRUE;
9356   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9357     rechts_frei = TRUE;
9358
9359   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9360       element == EL_EXPANDABLE_WALL_ANY)
9361   {
9362     if (oben_frei)
9363     {
9364       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9365       Store[ax][ay-1] = element;
9366       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9367       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9368         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9369                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9370       new_wall = TRUE;
9371     }
9372     if (unten_frei)
9373     {
9374       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9375       Store[ax][ay+1] = element;
9376       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9377       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9378         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9379                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9380       new_wall = TRUE;
9381     }
9382   }
9383
9384   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9385       element == EL_EXPANDABLE_WALL_ANY ||
9386       element == EL_EXPANDABLE_WALL ||
9387       element == EL_BD_EXPANDABLE_WALL)
9388   {
9389     if (links_frei)
9390     {
9391       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9392       Store[ax-1][ay] = element;
9393       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9394       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9395         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9396                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9397       new_wall = TRUE;
9398     }
9399
9400     if (rechts_frei)
9401     {
9402       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9403       Store[ax+1][ay] = element;
9404       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9405       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9406         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9407                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9408       new_wall = TRUE;
9409     }
9410   }
9411
9412   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9413     TEST_DrawLevelField(ax, ay);
9414
9415   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9416     oben_massiv = TRUE;
9417   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9418     unten_massiv = TRUE;
9419   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9420     links_massiv = TRUE;
9421   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9422     rechts_massiv = TRUE;
9423
9424   if (((oben_massiv && unten_massiv) ||
9425        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9426        element == EL_EXPANDABLE_WALL) &&
9427       ((links_massiv && rechts_massiv) ||
9428        element == EL_EXPANDABLE_WALL_VERTICAL))
9429     Feld[ax][ay] = EL_WALL;
9430
9431   if (new_wall)
9432     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9433 }
9434
9435 static void MauerAblegerStahl(int ax, int ay)
9436 {
9437   int element = Feld[ax][ay];
9438   int graphic = el2img(element);
9439   boolean oben_frei = FALSE, unten_frei = FALSE;
9440   boolean links_frei = FALSE, rechts_frei = FALSE;
9441   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9442   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9443   boolean new_wall = FALSE;
9444
9445   if (IS_ANIMATED(graphic))
9446     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9447
9448   if (!MovDelay[ax][ay])        // start building new wall
9449     MovDelay[ax][ay] = 6;
9450
9451   if (MovDelay[ax][ay])         // wait some time before building new wall
9452   {
9453     MovDelay[ax][ay]--;
9454     if (MovDelay[ax][ay])
9455       return;
9456   }
9457
9458   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9459     oben_frei = TRUE;
9460   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9461     unten_frei = TRUE;
9462   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9463     links_frei = TRUE;
9464   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9465     rechts_frei = TRUE;
9466
9467   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9468       element == EL_EXPANDABLE_STEELWALL_ANY)
9469   {
9470     if (oben_frei)
9471     {
9472       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9473       Store[ax][ay-1] = element;
9474       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9475       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9476         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9477                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9478       new_wall = TRUE;
9479     }
9480     if (unten_frei)
9481     {
9482       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9483       Store[ax][ay+1] = element;
9484       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9485       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9486         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9487                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9488       new_wall = TRUE;
9489     }
9490   }
9491
9492   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9493       element == EL_EXPANDABLE_STEELWALL_ANY)
9494   {
9495     if (links_frei)
9496     {
9497       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9498       Store[ax-1][ay] = element;
9499       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9500       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9501         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9502                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9503       new_wall = TRUE;
9504     }
9505
9506     if (rechts_frei)
9507     {
9508       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9509       Store[ax+1][ay] = element;
9510       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9511       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9512         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9513                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9514       new_wall = TRUE;
9515     }
9516   }
9517
9518   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9519     oben_massiv = TRUE;
9520   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9521     unten_massiv = TRUE;
9522   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9523     links_massiv = TRUE;
9524   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9525     rechts_massiv = TRUE;
9526
9527   if (((oben_massiv && unten_massiv) ||
9528        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9529       ((links_massiv && rechts_massiv) ||
9530        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9531     Feld[ax][ay] = EL_STEELWALL;
9532
9533   if (new_wall)
9534     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9535 }
9536
9537 static void CheckForDragon(int x, int y)
9538 {
9539   int i, j;
9540   boolean dragon_found = FALSE;
9541   static int xy[4][2] =
9542   {
9543     { 0, -1 },
9544     { -1, 0 },
9545     { +1, 0 },
9546     { 0, +1 }
9547   };
9548
9549   for (i = 0; i < NUM_DIRECTIONS; i++)
9550   {
9551     for (j = 0; j < 4; j++)
9552     {
9553       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9554
9555       if (IN_LEV_FIELD(xx, yy) &&
9556           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9557       {
9558         if (Feld[xx][yy] == EL_DRAGON)
9559           dragon_found = TRUE;
9560       }
9561       else
9562         break;
9563     }
9564   }
9565
9566   if (!dragon_found)
9567   {
9568     for (i = 0; i < NUM_DIRECTIONS; i++)
9569     {
9570       for (j = 0; j < 3; j++)
9571       {
9572         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9573   
9574         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9575         {
9576           Feld[xx][yy] = EL_EMPTY;
9577           TEST_DrawLevelField(xx, yy);
9578         }
9579         else
9580           break;
9581       }
9582     }
9583   }
9584 }
9585
9586 static void InitBuggyBase(int x, int y)
9587 {
9588   int element = Feld[x][y];
9589   int activating_delay = FRAMES_PER_SECOND / 4;
9590
9591   ChangeDelay[x][y] =
9592     (element == EL_SP_BUGGY_BASE ?
9593      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9594      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9595      activating_delay :
9596      element == EL_SP_BUGGY_BASE_ACTIVE ?
9597      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9598 }
9599
9600 static void WarnBuggyBase(int x, int y)
9601 {
9602   int i;
9603   static int xy[4][2] =
9604   {
9605     { 0, -1 },
9606     { -1, 0 },
9607     { +1, 0 },
9608     { 0, +1 }
9609   };
9610
9611   for (i = 0; i < NUM_DIRECTIONS; i++)
9612   {
9613     int xx = x + xy[i][0];
9614     int yy = y + xy[i][1];
9615
9616     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9617     {
9618       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9619
9620       break;
9621     }
9622   }
9623 }
9624
9625 static void InitTrap(int x, int y)
9626 {
9627   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9628 }
9629
9630 static void ActivateTrap(int x, int y)
9631 {
9632   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9633 }
9634
9635 static void ChangeActiveTrap(int x, int y)
9636 {
9637   int graphic = IMG_TRAP_ACTIVE;
9638
9639   // if new animation frame was drawn, correct crumbled sand border
9640   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9641     TEST_DrawLevelFieldCrumbled(x, y);
9642 }
9643
9644 static int getSpecialActionElement(int element, int number, int base_element)
9645 {
9646   return (element != EL_EMPTY ? element :
9647           number != -1 ? base_element + number - 1 :
9648           EL_EMPTY);
9649 }
9650
9651 static int getModifiedActionNumber(int value_old, int operator, int operand,
9652                                    int value_min, int value_max)
9653 {
9654   int value_new = (operator == CA_MODE_SET      ? operand :
9655                    operator == CA_MODE_ADD      ? value_old + operand :
9656                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9657                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9658                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9659                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9660                    value_old);
9661
9662   return (value_new < value_min ? value_min :
9663           value_new > value_max ? value_max :
9664           value_new);
9665 }
9666
9667 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9668 {
9669   struct ElementInfo *ei = &element_info[element];
9670   struct ElementChangeInfo *change = &ei->change_page[page];
9671   int target_element = change->target_element;
9672   int action_type = change->action_type;
9673   int action_mode = change->action_mode;
9674   int action_arg = change->action_arg;
9675   int action_element = change->action_element;
9676   int i;
9677
9678   if (!change->has_action)
9679     return;
9680
9681   // ---------- determine action paramater values -----------------------------
9682
9683   int level_time_value =
9684     (level.time > 0 ? TimeLeft :
9685      TimePlayed);
9686
9687   int action_arg_element_raw =
9688     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9689      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9690      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9691      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9692      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9693      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9694      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9695      EL_EMPTY);
9696   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9697
9698   int action_arg_direction =
9699     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9700      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9701      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9702      change->actual_trigger_side :
9703      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9704      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9705      MV_NONE);
9706
9707   int action_arg_number_min =
9708     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9709      CA_ARG_MIN);
9710
9711   int action_arg_number_max =
9712     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9713      action_type == CA_SET_LEVEL_GEMS ? 999 :
9714      action_type == CA_SET_LEVEL_TIME ? 9999 :
9715      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9716      action_type == CA_SET_CE_VALUE ? 9999 :
9717      action_type == CA_SET_CE_SCORE ? 9999 :
9718      CA_ARG_MAX);
9719
9720   int action_arg_number_reset =
9721     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9722      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9723      action_type == CA_SET_LEVEL_TIME ? level.time :
9724      action_type == CA_SET_LEVEL_SCORE ? 0 :
9725      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9726      action_type == CA_SET_CE_SCORE ? 0 :
9727      0);
9728
9729   int action_arg_number =
9730     (action_arg <= CA_ARG_MAX ? action_arg :
9731      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9732      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9733      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9734      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9735      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9736      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9737      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9738      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9739      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9740      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9741      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9742      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9743      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9744      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9745      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9746      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9747      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9748      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9749      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9750      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9751      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9752      -1);
9753
9754   int action_arg_number_old =
9755     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9756      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9757      action_type == CA_SET_LEVEL_SCORE ? game.score :
9758      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9759      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9760      0);
9761
9762   int action_arg_number_new =
9763     getModifiedActionNumber(action_arg_number_old,
9764                             action_mode, action_arg_number,
9765                             action_arg_number_min, action_arg_number_max);
9766
9767   int trigger_player_bits =
9768     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9769      change->actual_trigger_player_bits : change->trigger_player);
9770
9771   int action_arg_player_bits =
9772     (action_arg >= CA_ARG_PLAYER_1 &&
9773      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9774      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9775      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9776      PLAYER_BITS_ANY);
9777
9778   // ---------- execute action  -----------------------------------------------
9779
9780   switch (action_type)
9781   {
9782     case CA_NO_ACTION:
9783     {
9784       return;
9785     }
9786
9787     // ---------- level actions  ----------------------------------------------
9788
9789     case CA_RESTART_LEVEL:
9790     {
9791       game.restart_level = TRUE;
9792
9793       break;
9794     }
9795
9796     case CA_SHOW_ENVELOPE:
9797     {
9798       int element = getSpecialActionElement(action_arg_element,
9799                                             action_arg_number, EL_ENVELOPE_1);
9800
9801       if (IS_ENVELOPE(element))
9802         local_player->show_envelope = element;
9803
9804       break;
9805     }
9806
9807     case CA_SET_LEVEL_TIME:
9808     {
9809       if (level.time > 0)       // only modify limited time value
9810       {
9811         TimeLeft = action_arg_number_new;
9812
9813         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9814
9815         DisplayGameControlValues();
9816
9817         if (!TimeLeft && setup.time_limit)
9818           for (i = 0; i < MAX_PLAYERS; i++)
9819             KillPlayer(&stored_player[i]);
9820       }
9821
9822       break;
9823     }
9824
9825     case CA_SET_LEVEL_SCORE:
9826     {
9827       game.score = action_arg_number_new;
9828
9829       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9830
9831       DisplayGameControlValues();
9832
9833       break;
9834     }
9835
9836     case CA_SET_LEVEL_GEMS:
9837     {
9838       game.gems_still_needed = action_arg_number_new;
9839
9840       game.snapshot.collected_item = TRUE;
9841
9842       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9843
9844       DisplayGameControlValues();
9845
9846       break;
9847     }
9848
9849     case CA_SET_LEVEL_WIND:
9850     {
9851       game.wind_direction = action_arg_direction;
9852
9853       break;
9854     }
9855
9856     case CA_SET_LEVEL_RANDOM_SEED:
9857     {
9858       // ensure that setting a new random seed while playing is predictable
9859       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9860
9861       break;
9862     }
9863
9864     // ---------- player actions  ---------------------------------------------
9865
9866     case CA_MOVE_PLAYER:
9867     {
9868       // automatically move to the next field in specified direction
9869       for (i = 0; i < MAX_PLAYERS; i++)
9870         if (trigger_player_bits & (1 << i))
9871           stored_player[i].programmed_action = action_arg_direction;
9872
9873       break;
9874     }
9875
9876     case CA_EXIT_PLAYER:
9877     {
9878       for (i = 0; i < MAX_PLAYERS; i++)
9879         if (action_arg_player_bits & (1 << i))
9880           ExitPlayer(&stored_player[i]);
9881
9882       if (game.players_still_needed == 0)
9883         LevelSolved();
9884
9885       break;
9886     }
9887
9888     case CA_KILL_PLAYER:
9889     {
9890       for (i = 0; i < MAX_PLAYERS; i++)
9891         if (action_arg_player_bits & (1 << i))
9892           KillPlayer(&stored_player[i]);
9893
9894       break;
9895     }
9896
9897     case CA_SET_PLAYER_KEYS:
9898     {
9899       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9900       int element = getSpecialActionElement(action_arg_element,
9901                                             action_arg_number, EL_KEY_1);
9902
9903       if (IS_KEY(element))
9904       {
9905         for (i = 0; i < MAX_PLAYERS; i++)
9906         {
9907           if (trigger_player_bits & (1 << i))
9908           {
9909             stored_player[i].key[KEY_NR(element)] = key_state;
9910
9911             DrawGameDoorValues();
9912           }
9913         }
9914       }
9915
9916       break;
9917     }
9918
9919     case CA_SET_PLAYER_SPEED:
9920     {
9921       for (i = 0; i < MAX_PLAYERS; i++)
9922       {
9923         if (trigger_player_bits & (1 << i))
9924         {
9925           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9926
9927           if (action_arg == CA_ARG_SPEED_FASTER &&
9928               stored_player[i].cannot_move)
9929           {
9930             action_arg_number = STEPSIZE_VERY_SLOW;
9931           }
9932           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9933                    action_arg == CA_ARG_SPEED_FASTER)
9934           {
9935             action_arg_number = 2;
9936             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9937                            CA_MODE_MULTIPLY);
9938           }
9939           else if (action_arg == CA_ARG_NUMBER_RESET)
9940           {
9941             action_arg_number = level.initial_player_stepsize[i];
9942           }
9943
9944           move_stepsize =
9945             getModifiedActionNumber(move_stepsize,
9946                                     action_mode,
9947                                     action_arg_number,
9948                                     action_arg_number_min,
9949                                     action_arg_number_max);
9950
9951           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9952         }
9953       }
9954
9955       break;
9956     }
9957
9958     case CA_SET_PLAYER_SHIELD:
9959     {
9960       for (i = 0; i < MAX_PLAYERS; i++)
9961       {
9962         if (trigger_player_bits & (1 << i))
9963         {
9964           if (action_arg == CA_ARG_SHIELD_OFF)
9965           {
9966             stored_player[i].shield_normal_time_left = 0;
9967             stored_player[i].shield_deadly_time_left = 0;
9968           }
9969           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9970           {
9971             stored_player[i].shield_normal_time_left = 999999;
9972           }
9973           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9974           {
9975             stored_player[i].shield_normal_time_left = 999999;
9976             stored_player[i].shield_deadly_time_left = 999999;
9977           }
9978         }
9979       }
9980
9981       break;
9982     }
9983
9984     case CA_SET_PLAYER_GRAVITY:
9985     {
9986       for (i = 0; i < MAX_PLAYERS; i++)
9987       {
9988         if (trigger_player_bits & (1 << i))
9989         {
9990           stored_player[i].gravity =
9991             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9992              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9993              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9994              stored_player[i].gravity);
9995         }
9996       }
9997
9998       break;
9999     }
10000
10001     case CA_SET_PLAYER_ARTWORK:
10002     {
10003       for (i = 0; i < MAX_PLAYERS; i++)
10004       {
10005         if (trigger_player_bits & (1 << i))
10006         {
10007           int artwork_element = action_arg_element;
10008
10009           if (action_arg == CA_ARG_ELEMENT_RESET)
10010             artwork_element =
10011               (level.use_artwork_element[i] ? level.artwork_element[i] :
10012                stored_player[i].element_nr);
10013
10014           if (stored_player[i].artwork_element != artwork_element)
10015             stored_player[i].Frame = 0;
10016
10017           stored_player[i].artwork_element = artwork_element;
10018
10019           SetPlayerWaiting(&stored_player[i], FALSE);
10020
10021           // set number of special actions for bored and sleeping animation
10022           stored_player[i].num_special_action_bored =
10023             get_num_special_action(artwork_element,
10024                                    ACTION_BORING_1, ACTION_BORING_LAST);
10025           stored_player[i].num_special_action_sleeping =
10026             get_num_special_action(artwork_element,
10027                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10028         }
10029       }
10030
10031       break;
10032     }
10033
10034     case CA_SET_PLAYER_INVENTORY:
10035     {
10036       for (i = 0; i < MAX_PLAYERS; i++)
10037       {
10038         struct PlayerInfo *player = &stored_player[i];
10039         int j, k;
10040
10041         if (trigger_player_bits & (1 << i))
10042         {
10043           int inventory_element = action_arg_element;
10044
10045           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10046               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10047               action_arg == CA_ARG_ELEMENT_ACTION)
10048           {
10049             int element = inventory_element;
10050             int collect_count = element_info[element].collect_count_initial;
10051
10052             if (!IS_CUSTOM_ELEMENT(element))
10053               collect_count = 1;
10054
10055             if (collect_count == 0)
10056               player->inventory_infinite_element = element;
10057             else
10058               for (k = 0; k < collect_count; k++)
10059                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10060                   player->inventory_element[player->inventory_size++] =
10061                     element;
10062           }
10063           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10064                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10065                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10066           {
10067             if (player->inventory_infinite_element != EL_UNDEFINED &&
10068                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10069                                      action_arg_element_raw))
10070               player->inventory_infinite_element = EL_UNDEFINED;
10071
10072             for (k = 0, j = 0; j < player->inventory_size; j++)
10073             {
10074               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10075                                         action_arg_element_raw))
10076                 player->inventory_element[k++] = player->inventory_element[j];
10077             }
10078
10079             player->inventory_size = k;
10080           }
10081           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10082           {
10083             if (player->inventory_size > 0)
10084             {
10085               for (j = 0; j < player->inventory_size - 1; j++)
10086                 player->inventory_element[j] = player->inventory_element[j + 1];
10087
10088               player->inventory_size--;
10089             }
10090           }
10091           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10092           {
10093             if (player->inventory_size > 0)
10094               player->inventory_size--;
10095           }
10096           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10097           {
10098             player->inventory_infinite_element = EL_UNDEFINED;
10099             player->inventory_size = 0;
10100           }
10101           else if (action_arg == CA_ARG_INVENTORY_RESET)
10102           {
10103             player->inventory_infinite_element = EL_UNDEFINED;
10104             player->inventory_size = 0;
10105
10106             if (level.use_initial_inventory[i])
10107             {
10108               for (j = 0; j < level.initial_inventory_size[i]; j++)
10109               {
10110                 int element = level.initial_inventory_content[i][j];
10111                 int collect_count = element_info[element].collect_count_initial;
10112
10113                 if (!IS_CUSTOM_ELEMENT(element))
10114                   collect_count = 1;
10115
10116                 if (collect_count == 0)
10117                   player->inventory_infinite_element = element;
10118                 else
10119                   for (k = 0; k < collect_count; k++)
10120                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10121                       player->inventory_element[player->inventory_size++] =
10122                         element;
10123               }
10124             }
10125           }
10126         }
10127       }
10128
10129       break;
10130     }
10131
10132     // ---------- CE actions  -------------------------------------------------
10133
10134     case CA_SET_CE_VALUE:
10135     {
10136       int last_ce_value = CustomValue[x][y];
10137
10138       CustomValue[x][y] = action_arg_number_new;
10139
10140       if (CustomValue[x][y] != last_ce_value)
10141       {
10142         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10143         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10144
10145         if (CustomValue[x][y] == 0)
10146         {
10147           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10148           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10149         }
10150       }
10151
10152       break;
10153     }
10154
10155     case CA_SET_CE_SCORE:
10156     {
10157       int last_ce_score = ei->collect_score;
10158
10159       ei->collect_score = action_arg_number_new;
10160
10161       if (ei->collect_score != last_ce_score)
10162       {
10163         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10164         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10165
10166         if (ei->collect_score == 0)
10167         {
10168           int xx, yy;
10169
10170           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10171           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10172
10173           /*
10174             This is a very special case that seems to be a mixture between
10175             CheckElementChange() and CheckTriggeredElementChange(): while
10176             the first one only affects single elements that are triggered
10177             directly, the second one affects multiple elements in the playfield
10178             that are triggered indirectly by another element. This is a third
10179             case: Changing the CE score always affects multiple identical CEs,
10180             so every affected CE must be checked, not only the single CE for
10181             which the CE score was changed in the first place (as every instance
10182             of that CE shares the same CE score, and therefore also can change)!
10183           */
10184           SCAN_PLAYFIELD(xx, yy)
10185           {
10186             if (Feld[xx][yy] == element)
10187               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10188                                  CE_SCORE_GETS_ZERO);
10189           }
10190         }
10191       }
10192
10193       break;
10194     }
10195
10196     case CA_SET_CE_ARTWORK:
10197     {
10198       int artwork_element = action_arg_element;
10199       boolean reset_frame = FALSE;
10200       int xx, yy;
10201
10202       if (action_arg == CA_ARG_ELEMENT_RESET)
10203         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10204                            element);
10205
10206       if (ei->gfx_element != artwork_element)
10207         reset_frame = TRUE;
10208
10209       ei->gfx_element = artwork_element;
10210
10211       SCAN_PLAYFIELD(xx, yy)
10212       {
10213         if (Feld[xx][yy] == element)
10214         {
10215           if (reset_frame)
10216           {
10217             ResetGfxAnimation(xx, yy);
10218             ResetRandomAnimationValue(xx, yy);
10219           }
10220
10221           TEST_DrawLevelField(xx, yy);
10222         }
10223       }
10224
10225       break;
10226     }
10227
10228     // ---------- engine actions  ---------------------------------------------
10229
10230     case CA_SET_ENGINE_SCAN_MODE:
10231     {
10232       InitPlayfieldScanMode(action_arg);
10233
10234       break;
10235     }
10236
10237     default:
10238       break;
10239   }
10240 }
10241
10242 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10243 {
10244   int old_element = Feld[x][y];
10245   int new_element = GetElementFromGroupElement(element);
10246   int previous_move_direction = MovDir[x][y];
10247   int last_ce_value = CustomValue[x][y];
10248   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10249   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10250   boolean add_player_onto_element = (new_element_is_player &&
10251                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10252                                      IS_WALKABLE(old_element));
10253
10254   if (!add_player_onto_element)
10255   {
10256     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10257       RemoveMovingField(x, y);
10258     else
10259       RemoveField(x, y);
10260
10261     Feld[x][y] = new_element;
10262
10263     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10264       MovDir[x][y] = previous_move_direction;
10265
10266     if (element_info[new_element].use_last_ce_value)
10267       CustomValue[x][y] = last_ce_value;
10268
10269     InitField_WithBug1(x, y, FALSE);
10270
10271     new_element = Feld[x][y];   // element may have changed
10272
10273     ResetGfxAnimation(x, y);
10274     ResetRandomAnimationValue(x, y);
10275
10276     TEST_DrawLevelField(x, y);
10277
10278     if (GFX_CRUMBLED(new_element))
10279       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10280   }
10281
10282   // check if element under the player changes from accessible to unaccessible
10283   // (needed for special case of dropping element which then changes)
10284   // (must be checked after creating new element for walkable group elements)
10285   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10286       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10287   {
10288     Bang(x, y);
10289
10290     return;
10291   }
10292
10293   // "ChangeCount" not set yet to allow "entered by player" change one time
10294   if (new_element_is_player)
10295     RelocatePlayer(x, y, new_element);
10296
10297   if (is_change)
10298     ChangeCount[x][y]++;        // count number of changes in the same frame
10299
10300   TestIfBadThingTouchesPlayer(x, y);
10301   TestIfPlayerTouchesCustomElement(x, y);
10302   TestIfElementTouchesCustomElement(x, y);
10303 }
10304
10305 static void CreateField(int x, int y, int element)
10306 {
10307   CreateFieldExt(x, y, element, FALSE);
10308 }
10309
10310 static void CreateElementFromChange(int x, int y, int element)
10311 {
10312   element = GET_VALID_RUNTIME_ELEMENT(element);
10313
10314   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10315   {
10316     int old_element = Feld[x][y];
10317
10318     // prevent changed element from moving in same engine frame
10319     // unless both old and new element can either fall or move
10320     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10321         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10322       Stop[x][y] = TRUE;
10323   }
10324
10325   CreateFieldExt(x, y, element, TRUE);
10326 }
10327
10328 static boolean ChangeElement(int x, int y, int element, int page)
10329 {
10330   struct ElementInfo *ei = &element_info[element];
10331   struct ElementChangeInfo *change = &ei->change_page[page];
10332   int ce_value = CustomValue[x][y];
10333   int ce_score = ei->collect_score;
10334   int target_element;
10335   int old_element = Feld[x][y];
10336
10337   // always use default change event to prevent running into a loop
10338   if (ChangeEvent[x][y] == -1)
10339     ChangeEvent[x][y] = CE_DELAY;
10340
10341   if (ChangeEvent[x][y] == CE_DELAY)
10342   {
10343     // reset actual trigger element, trigger player and action element
10344     change->actual_trigger_element = EL_EMPTY;
10345     change->actual_trigger_player = EL_EMPTY;
10346     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10347     change->actual_trigger_side = CH_SIDE_NONE;
10348     change->actual_trigger_ce_value = 0;
10349     change->actual_trigger_ce_score = 0;
10350   }
10351
10352   // do not change elements more than a specified maximum number of changes
10353   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10354     return FALSE;
10355
10356   ChangeCount[x][y]++;          // count number of changes in the same frame
10357
10358   if (change->explode)
10359   {
10360     Bang(x, y);
10361
10362     return TRUE;
10363   }
10364
10365   if (change->use_target_content)
10366   {
10367     boolean complete_replace = TRUE;
10368     boolean can_replace[3][3];
10369     int xx, yy;
10370
10371     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10372     {
10373       boolean is_empty;
10374       boolean is_walkable;
10375       boolean is_diggable;
10376       boolean is_collectible;
10377       boolean is_removable;
10378       boolean is_destructible;
10379       int ex = x + xx - 1;
10380       int ey = y + yy - 1;
10381       int content_element = change->target_content.e[xx][yy];
10382       int e;
10383
10384       can_replace[xx][yy] = TRUE;
10385
10386       if (ex == x && ey == y)   // do not check changing element itself
10387         continue;
10388
10389       if (content_element == EL_EMPTY_SPACE)
10390       {
10391         can_replace[xx][yy] = FALSE;    // do not replace border with space
10392
10393         continue;
10394       }
10395
10396       if (!IN_LEV_FIELD(ex, ey))
10397       {
10398         can_replace[xx][yy] = FALSE;
10399         complete_replace = FALSE;
10400
10401         continue;
10402       }
10403
10404       e = Feld[ex][ey];
10405
10406       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10407         e = MovingOrBlocked2Element(ex, ey);
10408
10409       is_empty = (IS_FREE(ex, ey) ||
10410                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10411
10412       is_walkable     = (is_empty || IS_WALKABLE(e));
10413       is_diggable     = (is_empty || IS_DIGGABLE(e));
10414       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10415       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10416       is_removable    = (is_diggable || is_collectible);
10417
10418       can_replace[xx][yy] =
10419         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10420           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10421           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10422           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10423           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10424           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10425          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10426
10427       if (!can_replace[xx][yy])
10428         complete_replace = FALSE;
10429     }
10430
10431     if (!change->only_if_complete || complete_replace)
10432     {
10433       boolean something_has_changed = FALSE;
10434
10435       if (change->only_if_complete && change->use_random_replace &&
10436           RND(100) < change->random_percentage)
10437         return FALSE;
10438
10439       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10440       {
10441         int ex = x + xx - 1;
10442         int ey = y + yy - 1;
10443         int content_element;
10444
10445         if (can_replace[xx][yy] && (!change->use_random_replace ||
10446                                     RND(100) < change->random_percentage))
10447         {
10448           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10449             RemoveMovingField(ex, ey);
10450
10451           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10452
10453           content_element = change->target_content.e[xx][yy];
10454           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10455                                               ce_value, ce_score);
10456
10457           CreateElementFromChange(ex, ey, target_element);
10458
10459           something_has_changed = TRUE;
10460
10461           // for symmetry reasons, freeze newly created border elements
10462           if (ex != x || ey != y)
10463             Stop[ex][ey] = TRUE;        // no more moving in this frame
10464         }
10465       }
10466
10467       if (something_has_changed)
10468       {
10469         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10470         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10471       }
10472     }
10473   }
10474   else
10475   {
10476     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10477                                         ce_value, ce_score);
10478
10479     if (element == EL_DIAGONAL_GROWING ||
10480         element == EL_DIAGONAL_SHRINKING)
10481     {
10482       target_element = Store[x][y];
10483
10484       Store[x][y] = EL_EMPTY;
10485     }
10486
10487     CreateElementFromChange(x, y, target_element);
10488
10489     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10490     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10491   }
10492
10493   // this uses direct change before indirect change
10494   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10495
10496   return TRUE;
10497 }
10498
10499 static void HandleElementChange(int x, int y, int page)
10500 {
10501   int element = MovingOrBlocked2Element(x, y);
10502   struct ElementInfo *ei = &element_info[element];
10503   struct ElementChangeInfo *change = &ei->change_page[page];
10504   boolean handle_action_before_change = FALSE;
10505
10506 #ifdef DEBUG
10507   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10508       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10509   {
10510     printf("\n\n");
10511     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10512            x, y, element, element_info[element].token_name);
10513     printf("HandleElementChange(): This should never happen!\n");
10514     printf("\n\n");
10515   }
10516 #endif
10517
10518   // this can happen with classic bombs on walkable, changing elements
10519   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10520   {
10521     return;
10522   }
10523
10524   if (ChangeDelay[x][y] == 0)           // initialize element change
10525   {
10526     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10527
10528     if (change->can_change)
10529     {
10530       // !!! not clear why graphic animation should be reset at all here !!!
10531       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10532       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10533
10534       /*
10535         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10536
10537         When using an animation frame delay of 1 (this only happens with
10538         "sp_zonk.moving.left/right" in the classic graphics), the default
10539         (non-moving) animation shows wrong animation frames (while the
10540         moving animation, like "sp_zonk.moving.left/right", is correct,
10541         so this graphical bug never shows up with the classic graphics).
10542         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10543         be drawn instead of the correct frames 0,1,2,3. This is caused by
10544         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10545         an element change: First when the change delay ("ChangeDelay[][]")
10546         counter has reached zero after decrementing, then a second time in
10547         the next frame (after "GfxFrame[][]" was already incremented) when
10548         "ChangeDelay[][]" is reset to the initial delay value again.
10549
10550         This causes frame 0 to be drawn twice, while the last frame won't
10551         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10552
10553         As some animations may already be cleverly designed around this bug
10554         (at least the "Snake Bite" snake tail animation does this), it cannot
10555         simply be fixed here without breaking such existing animations.
10556         Unfortunately, it cannot easily be detected if a graphics set was
10557         designed "before" or "after" the bug was fixed. As a workaround,
10558         a new graphics set option "game.graphics_engine_version" was added
10559         to be able to specify the game's major release version for which the
10560         graphics set was designed, which can then be used to decide if the
10561         bugfix should be used (version 4 and above) or not (version 3 or
10562         below, or if no version was specified at all, as with old sets).
10563
10564         (The wrong/fixed animation frames can be tested with the test level set
10565         "test_gfxframe" and level "000", which contains a specially prepared
10566         custom element at level position (x/y) == (11/9) which uses the zonk
10567         animation mentioned above. Using "game.graphics_engine_version: 4"
10568         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10569         This can also be seen from the debug output for this test element.)
10570       */
10571
10572       // when a custom element is about to change (for example by change delay),
10573       // do not reset graphic animation when the custom element is moving
10574       if (game.graphics_engine_version < 4 &&
10575           !IS_MOVING(x, y))
10576       {
10577         ResetGfxAnimation(x, y);
10578         ResetRandomAnimationValue(x, y);
10579       }
10580
10581       if (change->pre_change_function)
10582         change->pre_change_function(x, y);
10583     }
10584   }
10585
10586   ChangeDelay[x][y]--;
10587
10588   if (ChangeDelay[x][y] != 0)           // continue element change
10589   {
10590     if (change->can_change)
10591     {
10592       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10593
10594       if (IS_ANIMATED(graphic))
10595         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10596
10597       if (change->change_function)
10598         change->change_function(x, y);
10599     }
10600   }
10601   else                                  // finish element change
10602   {
10603     if (ChangePage[x][y] != -1)         // remember page from delayed change
10604     {
10605       page = ChangePage[x][y];
10606       ChangePage[x][y] = -1;
10607
10608       change = &ei->change_page[page];
10609     }
10610
10611     if (IS_MOVING(x, y))                // never change a running system ;-)
10612     {
10613       ChangeDelay[x][y] = 1;            // try change after next move step
10614       ChangePage[x][y] = page;          // remember page to use for change
10615
10616       return;
10617     }
10618
10619     // special case: set new level random seed before changing element
10620     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10621       handle_action_before_change = TRUE;
10622
10623     if (change->has_action && handle_action_before_change)
10624       ExecuteCustomElementAction(x, y, element, page);
10625
10626     if (change->can_change)
10627     {
10628       if (ChangeElement(x, y, element, page))
10629       {
10630         if (change->post_change_function)
10631           change->post_change_function(x, y);
10632       }
10633     }
10634
10635     if (change->has_action && !handle_action_before_change)
10636       ExecuteCustomElementAction(x, y, element, page);
10637   }
10638 }
10639
10640 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10641                                               int trigger_element,
10642                                               int trigger_event,
10643                                               int trigger_player,
10644                                               int trigger_side,
10645                                               int trigger_page)
10646 {
10647   boolean change_done_any = FALSE;
10648   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10649   int i;
10650
10651   if (!(trigger_events[trigger_element][trigger_event]))
10652     return FALSE;
10653
10654   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10655
10656   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10657   {
10658     int element = EL_CUSTOM_START + i;
10659     boolean change_done = FALSE;
10660     int p;
10661
10662     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10663         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10664       continue;
10665
10666     for (p = 0; p < element_info[element].num_change_pages; p++)
10667     {
10668       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10669
10670       if (change->can_change_or_has_action &&
10671           change->has_event[trigger_event] &&
10672           change->trigger_side & trigger_side &&
10673           change->trigger_player & trigger_player &&
10674           change->trigger_page & trigger_page_bits &&
10675           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10676       {
10677         change->actual_trigger_element = trigger_element;
10678         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10679         change->actual_trigger_player_bits = trigger_player;
10680         change->actual_trigger_side = trigger_side;
10681         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10682         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10683
10684         if ((change->can_change && !change_done) || change->has_action)
10685         {
10686           int x, y;
10687
10688           SCAN_PLAYFIELD(x, y)
10689           {
10690             if (Feld[x][y] == element)
10691             {
10692               if (change->can_change && !change_done)
10693               {
10694                 // if element already changed in this frame, not only prevent
10695                 // another element change (checked in ChangeElement()), but
10696                 // also prevent additional element actions for this element
10697
10698                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10699                     !level.use_action_after_change_bug)
10700                   continue;
10701
10702                 ChangeDelay[x][y] = 1;
10703                 ChangeEvent[x][y] = trigger_event;
10704
10705                 HandleElementChange(x, y, p);
10706               }
10707               else if (change->has_action)
10708               {
10709                 // if element already changed in this frame, not only prevent
10710                 // another element change (checked in ChangeElement()), but
10711                 // also prevent additional element actions for this element
10712
10713                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10714                     !level.use_action_after_change_bug)
10715                   continue;
10716
10717                 ExecuteCustomElementAction(x, y, element, p);
10718                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10719               }
10720             }
10721           }
10722
10723           if (change->can_change)
10724           {
10725             change_done = TRUE;
10726             change_done_any = TRUE;
10727           }
10728         }
10729       }
10730     }
10731   }
10732
10733   RECURSION_LOOP_DETECTION_END();
10734
10735   return change_done_any;
10736 }
10737
10738 static boolean CheckElementChangeExt(int x, int y,
10739                                      int element,
10740                                      int trigger_element,
10741                                      int trigger_event,
10742                                      int trigger_player,
10743                                      int trigger_side)
10744 {
10745   boolean change_done = FALSE;
10746   int p;
10747
10748   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10749       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10750     return FALSE;
10751
10752   if (Feld[x][y] == EL_BLOCKED)
10753   {
10754     Blocked2Moving(x, y, &x, &y);
10755     element = Feld[x][y];
10756   }
10757
10758   // check if element has already changed or is about to change after moving
10759   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10760        Feld[x][y] != element) ||
10761
10762       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10763        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10764         ChangePage[x][y] != -1)))
10765     return FALSE;
10766
10767   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10768
10769   for (p = 0; p < element_info[element].num_change_pages; p++)
10770   {
10771     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10772
10773     /* check trigger element for all events where the element that is checked
10774        for changing interacts with a directly adjacent element -- this is
10775        different to element changes that affect other elements to change on the
10776        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10777     boolean check_trigger_element =
10778       (trigger_event == CE_TOUCHING_X ||
10779        trigger_event == CE_HITTING_X ||
10780        trigger_event == CE_HIT_BY_X ||
10781        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10782
10783     if (change->can_change_or_has_action &&
10784         change->has_event[trigger_event] &&
10785         change->trigger_side & trigger_side &&
10786         change->trigger_player & trigger_player &&
10787         (!check_trigger_element ||
10788          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10789     {
10790       change->actual_trigger_element = trigger_element;
10791       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10792       change->actual_trigger_player_bits = trigger_player;
10793       change->actual_trigger_side = trigger_side;
10794       change->actual_trigger_ce_value = CustomValue[x][y];
10795       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10796
10797       // special case: trigger element not at (x,y) position for some events
10798       if (check_trigger_element)
10799       {
10800         static struct
10801         {
10802           int dx, dy;
10803         } move_xy[] =
10804           {
10805             {  0,  0 },
10806             { -1,  0 },
10807             { +1,  0 },
10808             {  0,  0 },
10809             {  0, -1 },
10810             {  0,  0 }, { 0, 0 }, { 0, 0 },
10811             {  0, +1 }
10812           };
10813
10814         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10815         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10816
10817         change->actual_trigger_ce_value = CustomValue[xx][yy];
10818         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10819       }
10820
10821       if (change->can_change && !change_done)
10822       {
10823         ChangeDelay[x][y] = 1;
10824         ChangeEvent[x][y] = trigger_event;
10825
10826         HandleElementChange(x, y, p);
10827
10828         change_done = TRUE;
10829       }
10830       else if (change->has_action)
10831       {
10832         ExecuteCustomElementAction(x, y, element, p);
10833         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10834       }
10835     }
10836   }
10837
10838   RECURSION_LOOP_DETECTION_END();
10839
10840   return change_done;
10841 }
10842
10843 static void PlayPlayerSound(struct PlayerInfo *player)
10844 {
10845   int jx = player->jx, jy = player->jy;
10846   int sound_element = player->artwork_element;
10847   int last_action = player->last_action_waiting;
10848   int action = player->action_waiting;
10849
10850   if (player->is_waiting)
10851   {
10852     if (action != last_action)
10853       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10854     else
10855       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10856   }
10857   else
10858   {
10859     if (action != last_action)
10860       StopSound(element_info[sound_element].sound[last_action]);
10861
10862     if (last_action == ACTION_SLEEPING)
10863       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10864   }
10865 }
10866
10867 static void PlayAllPlayersSound(void)
10868 {
10869   int i;
10870
10871   for (i = 0; i < MAX_PLAYERS; i++)
10872     if (stored_player[i].active)
10873       PlayPlayerSound(&stored_player[i]);
10874 }
10875
10876 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10877 {
10878   boolean last_waiting = player->is_waiting;
10879   int move_dir = player->MovDir;
10880
10881   player->dir_waiting = move_dir;
10882   player->last_action_waiting = player->action_waiting;
10883
10884   if (is_waiting)
10885   {
10886     if (!last_waiting)          // not waiting -> waiting
10887     {
10888       player->is_waiting = TRUE;
10889
10890       player->frame_counter_bored =
10891         FrameCounter +
10892         game.player_boring_delay_fixed +
10893         GetSimpleRandom(game.player_boring_delay_random);
10894       player->frame_counter_sleeping =
10895         FrameCounter +
10896         game.player_sleeping_delay_fixed +
10897         GetSimpleRandom(game.player_sleeping_delay_random);
10898
10899       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10900     }
10901
10902     if (game.player_sleeping_delay_fixed +
10903         game.player_sleeping_delay_random > 0 &&
10904         player->anim_delay_counter == 0 &&
10905         player->post_delay_counter == 0 &&
10906         FrameCounter >= player->frame_counter_sleeping)
10907       player->is_sleeping = TRUE;
10908     else if (game.player_boring_delay_fixed +
10909              game.player_boring_delay_random > 0 &&
10910              FrameCounter >= player->frame_counter_bored)
10911       player->is_bored = TRUE;
10912
10913     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10914                               player->is_bored ? ACTION_BORING :
10915                               ACTION_WAITING);
10916
10917     if (player->is_sleeping && player->use_murphy)
10918     {
10919       // special case for sleeping Murphy when leaning against non-free tile
10920
10921       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10922           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10923            !IS_MOVING(player->jx - 1, player->jy)))
10924         move_dir = MV_LEFT;
10925       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10926                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10927                 !IS_MOVING(player->jx + 1, player->jy)))
10928         move_dir = MV_RIGHT;
10929       else
10930         player->is_sleeping = FALSE;
10931
10932       player->dir_waiting = move_dir;
10933     }
10934
10935     if (player->is_sleeping)
10936     {
10937       if (player->num_special_action_sleeping > 0)
10938       {
10939         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10940         {
10941           int last_special_action = player->special_action_sleeping;
10942           int num_special_action = player->num_special_action_sleeping;
10943           int special_action =
10944             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10945              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10946              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10947              last_special_action + 1 : ACTION_SLEEPING);
10948           int special_graphic =
10949             el_act_dir2img(player->artwork_element, special_action, move_dir);
10950
10951           player->anim_delay_counter =
10952             graphic_info[special_graphic].anim_delay_fixed +
10953             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10954           player->post_delay_counter =
10955             graphic_info[special_graphic].post_delay_fixed +
10956             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10957
10958           player->special_action_sleeping = special_action;
10959         }
10960
10961         if (player->anim_delay_counter > 0)
10962         {
10963           player->action_waiting = player->special_action_sleeping;
10964           player->anim_delay_counter--;
10965         }
10966         else if (player->post_delay_counter > 0)
10967         {
10968           player->post_delay_counter--;
10969         }
10970       }
10971     }
10972     else if (player->is_bored)
10973     {
10974       if (player->num_special_action_bored > 0)
10975       {
10976         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10977         {
10978           int special_action =
10979             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10980           int special_graphic =
10981             el_act_dir2img(player->artwork_element, special_action, move_dir);
10982
10983           player->anim_delay_counter =
10984             graphic_info[special_graphic].anim_delay_fixed +
10985             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10986           player->post_delay_counter =
10987             graphic_info[special_graphic].post_delay_fixed +
10988             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10989
10990           player->special_action_bored = special_action;
10991         }
10992
10993         if (player->anim_delay_counter > 0)
10994         {
10995           player->action_waiting = player->special_action_bored;
10996           player->anim_delay_counter--;
10997         }
10998         else if (player->post_delay_counter > 0)
10999         {
11000           player->post_delay_counter--;
11001         }
11002       }
11003     }
11004   }
11005   else if (last_waiting)        // waiting -> not waiting
11006   {
11007     player->is_waiting = FALSE;
11008     player->is_bored = FALSE;
11009     player->is_sleeping = FALSE;
11010
11011     player->frame_counter_bored = -1;
11012     player->frame_counter_sleeping = -1;
11013
11014     player->anim_delay_counter = 0;
11015     player->post_delay_counter = 0;
11016
11017     player->dir_waiting = player->MovDir;
11018     player->action_waiting = ACTION_DEFAULT;
11019
11020     player->special_action_bored = ACTION_DEFAULT;
11021     player->special_action_sleeping = ACTION_DEFAULT;
11022   }
11023 }
11024
11025 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11026 {
11027   if ((!player->is_moving  && player->was_moving) ||
11028       (player->MovPos == 0 && player->was_moving) ||
11029       (player->is_snapping && !player->was_snapping) ||
11030       (player->is_dropping && !player->was_dropping))
11031   {
11032     if (!CheckSaveEngineSnapshotToList())
11033       return;
11034
11035     player->was_moving = FALSE;
11036     player->was_snapping = TRUE;
11037     player->was_dropping = TRUE;
11038   }
11039   else
11040   {
11041     if (player->is_moving)
11042       player->was_moving = TRUE;
11043
11044     if (!player->is_snapping)
11045       player->was_snapping = FALSE;
11046
11047     if (!player->is_dropping)
11048       player->was_dropping = FALSE;
11049   }
11050 }
11051
11052 static void CheckSingleStepMode(struct PlayerInfo *player)
11053 {
11054   if (tape.single_step && tape.recording && !tape.pausing)
11055   {
11056     /* as it is called "single step mode", just return to pause mode when the
11057        player stopped moving after one tile (or never starts moving at all) */
11058     if (!player->is_moving &&
11059         !player->is_pushing &&
11060         !player->is_dropping_pressed)
11061     {
11062       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11063       SnapField(player, 0, 0);                  // stop snapping
11064     }
11065   }
11066
11067   CheckSaveEngineSnapshot(player);
11068 }
11069
11070 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11071 {
11072   int left      = player_action & JOY_LEFT;
11073   int right     = player_action & JOY_RIGHT;
11074   int up        = player_action & JOY_UP;
11075   int down      = player_action & JOY_DOWN;
11076   int button1   = player_action & JOY_BUTTON_1;
11077   int button2   = player_action & JOY_BUTTON_2;
11078   int dx        = (left ? -1 : right ? 1 : 0);
11079   int dy        = (up   ? -1 : down  ? 1 : 0);
11080
11081   if (!player->active || tape.pausing)
11082     return 0;
11083
11084   if (player_action)
11085   {
11086     if (button1)
11087       SnapField(player, dx, dy);
11088     else
11089     {
11090       if (button2)
11091         DropElement(player);
11092
11093       MovePlayer(player, dx, dy);
11094     }
11095
11096     CheckSingleStepMode(player);
11097
11098     SetPlayerWaiting(player, FALSE);
11099
11100     return player_action;
11101   }
11102   else
11103   {
11104     // no actions for this player (no input at player's configured device)
11105
11106     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11107     SnapField(player, 0, 0);
11108     CheckGravityMovementWhenNotMoving(player);
11109
11110     if (player->MovPos == 0)
11111       SetPlayerWaiting(player, TRUE);
11112
11113     if (player->MovPos == 0)    // needed for tape.playing
11114       player->is_moving = FALSE;
11115
11116     player->is_dropping = FALSE;
11117     player->is_dropping_pressed = FALSE;
11118     player->drop_pressed_delay = 0;
11119
11120     CheckSingleStepMode(player);
11121
11122     return 0;
11123   }
11124 }
11125
11126 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11127                                          byte *tape_action)
11128 {
11129   if (!tape.use_mouse)
11130     return;
11131
11132   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11133   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11134   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11135 }
11136
11137 static void SetTapeActionFromMouseAction(byte *tape_action,
11138                                          struct MouseActionInfo *mouse_action)
11139 {
11140   if (!tape.use_mouse)
11141     return;
11142
11143   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11144   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11145   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11146 }
11147
11148 static void CheckLevelSolved(void)
11149 {
11150   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11151   {
11152     if (game_em.level_solved &&
11153         !game_em.game_over)                             // game won
11154     {
11155       LevelSolved();
11156
11157       game_em.game_over = TRUE;
11158
11159       game.all_players_gone = TRUE;
11160     }
11161
11162     if (game_em.game_over)                              // game lost
11163       game.all_players_gone = TRUE;
11164   }
11165   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11166   {
11167     if (game_sp.level_solved &&
11168         !game_sp.game_over)                             // game won
11169     {
11170       LevelSolved();
11171
11172       game_sp.game_over = TRUE;
11173
11174       game.all_players_gone = TRUE;
11175     }
11176
11177     if (game_sp.game_over)                              // game lost
11178       game.all_players_gone = TRUE;
11179   }
11180   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11181   {
11182     if (game_mm.level_solved &&
11183         !game_mm.game_over)                             // game won
11184     {
11185       LevelSolved();
11186
11187       game_mm.game_over = TRUE;
11188
11189       game.all_players_gone = TRUE;
11190     }
11191
11192     if (game_mm.game_over)                              // game lost
11193       game.all_players_gone = TRUE;
11194   }
11195 }
11196
11197 static void CheckLevelTime(void)
11198 {
11199   int i;
11200
11201   if (TimeFrames >= FRAMES_PER_SECOND)
11202   {
11203     TimeFrames = 0;
11204     TapeTime++;
11205
11206     for (i = 0; i < MAX_PLAYERS; i++)
11207     {
11208       struct PlayerInfo *player = &stored_player[i];
11209
11210       if (SHIELD_ON(player))
11211       {
11212         player->shield_normal_time_left--;
11213
11214         if (player->shield_deadly_time_left > 0)
11215           player->shield_deadly_time_left--;
11216       }
11217     }
11218
11219     if (!game.LevelSolved && !level.use_step_counter)
11220     {
11221       TimePlayed++;
11222
11223       if (TimeLeft > 0)
11224       {
11225         TimeLeft--;
11226
11227         if (TimeLeft <= 10 && setup.time_limit)
11228           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11229
11230         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11231            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11232
11233         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11234
11235         if (!TimeLeft && setup.time_limit)
11236         {
11237           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11238             level.native_em_level->lev->killed_out_of_time = TRUE;
11239           else
11240             for (i = 0; i < MAX_PLAYERS; i++)
11241               KillPlayer(&stored_player[i]);
11242         }
11243       }
11244       else if (game.no_time_limit && !game.all_players_gone)
11245       {
11246         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11247       }
11248
11249       level.native_em_level->lev->time =
11250         (game.no_time_limit ? TimePlayed : TimeLeft);
11251     }
11252
11253     if (tape.recording || tape.playing)
11254       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11255   }
11256
11257   if (tape.recording || tape.playing)
11258     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11259
11260   UpdateAndDisplayGameControlValues();
11261 }
11262
11263 void AdvanceFrameAndPlayerCounters(int player_nr)
11264 {
11265   int i;
11266
11267   // advance frame counters (global frame counter and time frame counter)
11268   FrameCounter++;
11269   TimeFrames++;
11270
11271   // advance player counters (counters for move delay, move animation etc.)
11272   for (i = 0; i < MAX_PLAYERS; i++)
11273   {
11274     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11275     int move_delay_value = stored_player[i].move_delay_value;
11276     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11277
11278     if (!advance_player_counters)       // not all players may be affected
11279       continue;
11280
11281     if (move_frames == 0)       // less than one move per game frame
11282     {
11283       int stepsize = TILEX / move_delay_value;
11284       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11285       int count = (stored_player[i].is_moving ?
11286                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11287
11288       if (count % delay == 0)
11289         move_frames = 1;
11290     }
11291
11292     stored_player[i].Frame += move_frames;
11293
11294     if (stored_player[i].MovPos != 0)
11295       stored_player[i].StepFrame += move_frames;
11296
11297     if (stored_player[i].move_delay > 0)
11298       stored_player[i].move_delay--;
11299
11300     // due to bugs in previous versions, counter must count up, not down
11301     if (stored_player[i].push_delay != -1)
11302       stored_player[i].push_delay++;
11303
11304     if (stored_player[i].drop_delay > 0)
11305       stored_player[i].drop_delay--;
11306
11307     if (stored_player[i].is_dropping_pressed)
11308       stored_player[i].drop_pressed_delay++;
11309   }
11310 }
11311
11312 void StartGameActions(boolean init_network_game, boolean record_tape,
11313                       int random_seed)
11314 {
11315   unsigned int new_random_seed = InitRND(random_seed);
11316
11317   if (record_tape)
11318     TapeStartRecording(new_random_seed);
11319
11320   if (init_network_game)
11321   {
11322     SendToServer_LevelFile();
11323     SendToServer_StartPlaying();
11324
11325     return;
11326   }
11327
11328   InitGame();
11329 }
11330
11331 static void GameActionsExt(void)
11332 {
11333 #if 0
11334   static unsigned int game_frame_delay = 0;
11335 #endif
11336   unsigned int game_frame_delay_value;
11337   byte *recorded_player_action;
11338   byte summarized_player_action = 0;
11339   byte tape_action[MAX_PLAYERS];
11340   int i;
11341
11342   // detect endless loops, caused by custom element programming
11343   if (recursion_loop_detected && recursion_loop_depth == 0)
11344   {
11345     char *message = getStringCat3("Internal Error! Element ",
11346                                   EL_NAME(recursion_loop_element),
11347                                   " caused endless loop! Quit the game?");
11348
11349     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11350           EL_NAME(recursion_loop_element));
11351
11352     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11353
11354     recursion_loop_detected = FALSE;    // if game should be continued
11355
11356     free(message);
11357
11358     return;
11359   }
11360
11361   if (game.restart_level)
11362     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11363
11364   CheckLevelSolved();
11365
11366   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11367     GameWon();
11368
11369   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11370     TapeStop();
11371
11372   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11373     return;
11374
11375   game_frame_delay_value =
11376     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11377
11378   if (tape.playing && tape.warp_forward && !tape.pausing)
11379     game_frame_delay_value = 0;
11380
11381   SetVideoFrameDelay(game_frame_delay_value);
11382
11383   // (de)activate virtual buttons depending on current game status
11384   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11385   {
11386     if (game.all_players_gone)  // if no players there to be controlled anymore
11387       SetOverlayActive(FALSE);
11388     else if (!tape.playing)     // if game continues after tape stopped playing
11389       SetOverlayActive(TRUE);
11390   }
11391
11392 #if 0
11393 #if 0
11394   // ---------- main game synchronization point ----------
11395
11396   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11397
11398   printf("::: skip == %d\n", skip);
11399
11400 #else
11401   // ---------- main game synchronization point ----------
11402
11403   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11404 #endif
11405 #endif
11406
11407   if (network_playing && !network_player_action_received)
11408   {
11409     // try to get network player actions in time
11410
11411     // last chance to get network player actions without main loop delay
11412     HandleNetworking();
11413
11414     // game was quit by network peer
11415     if (game_status != GAME_MODE_PLAYING)
11416       return;
11417
11418     // check if network player actions still missing and game still running
11419     if (!network_player_action_received && !checkGameEnded())
11420       return;           // failed to get network player actions in time
11421
11422     // do not yet reset "network_player_action_received" (for tape.pausing)
11423   }
11424
11425   if (tape.pausing)
11426     return;
11427
11428   // at this point we know that we really continue executing the game
11429
11430   network_player_action_received = FALSE;
11431
11432   // when playing tape, read previously recorded player input from tape data
11433   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11434
11435   local_player->effective_mouse_action = local_player->mouse_action;
11436
11437   if (recorded_player_action != NULL)
11438     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11439                                  recorded_player_action);
11440
11441   // TapePlayAction() may return NULL when toggling to "pause before death"
11442   if (tape.pausing)
11443     return;
11444
11445   if (tape.set_centered_player)
11446   {
11447     game.centered_player_nr_next = tape.centered_player_nr_next;
11448     game.set_centered_player = TRUE;
11449   }
11450
11451   for (i = 0; i < MAX_PLAYERS; i++)
11452   {
11453     summarized_player_action |= stored_player[i].action;
11454
11455     if (!network_playing && (game.team_mode || tape.playing))
11456       stored_player[i].effective_action = stored_player[i].action;
11457   }
11458
11459   if (network_playing && !checkGameEnded())
11460     SendToServer_MovePlayer(summarized_player_action);
11461
11462   // summarize all actions at local players mapped input device position
11463   // (this allows using different input devices in single player mode)
11464   if (!network.enabled && !game.team_mode)
11465     stored_player[map_player_action[local_player->index_nr]].effective_action =
11466       summarized_player_action;
11467
11468   if (tape.recording &&
11469       setup.team_mode &&
11470       setup.input_on_focus &&
11471       game.centered_player_nr != -1)
11472   {
11473     for (i = 0; i < MAX_PLAYERS; i++)
11474       stored_player[i].effective_action =
11475         (i == game.centered_player_nr ? summarized_player_action : 0);
11476   }
11477
11478   if (recorded_player_action != NULL)
11479     for (i = 0; i < MAX_PLAYERS; i++)
11480       stored_player[i].effective_action = recorded_player_action[i];
11481
11482   for (i = 0; i < MAX_PLAYERS; i++)
11483   {
11484     tape_action[i] = stored_player[i].effective_action;
11485
11486     /* (this may happen in the RND game engine if a player was not present on
11487        the playfield on level start, but appeared later from a custom element */
11488     if (setup.team_mode &&
11489         tape.recording &&
11490         tape_action[i] &&
11491         !tape.player_participates[i])
11492       tape.player_participates[i] = TRUE;
11493   }
11494
11495   SetTapeActionFromMouseAction(tape_action,
11496                                &local_player->effective_mouse_action);
11497
11498   // only record actions from input devices, but not programmed actions
11499   if (tape.recording)
11500     TapeRecordAction(tape_action);
11501
11502   // remember if game was played (especially after tape stopped playing)
11503   if (!tape.playing && summarized_player_action)
11504     game.GamePlayed = TRUE;
11505
11506 #if USE_NEW_PLAYER_ASSIGNMENTS
11507   // !!! also map player actions in single player mode !!!
11508   // if (game.team_mode)
11509   if (1)
11510   {
11511     byte mapped_action[MAX_PLAYERS];
11512
11513 #if DEBUG_PLAYER_ACTIONS
11514     printf(":::");
11515     for (i = 0; i < MAX_PLAYERS; i++)
11516       printf(" %d, ", stored_player[i].effective_action);
11517 #endif
11518
11519     for (i = 0; i < MAX_PLAYERS; i++)
11520       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11521
11522     for (i = 0; i < MAX_PLAYERS; i++)
11523       stored_player[i].effective_action = mapped_action[i];
11524
11525 #if DEBUG_PLAYER_ACTIONS
11526     printf(" =>");
11527     for (i = 0; i < MAX_PLAYERS; i++)
11528       printf(" %d, ", stored_player[i].effective_action);
11529     printf("\n");
11530 #endif
11531   }
11532 #if DEBUG_PLAYER_ACTIONS
11533   else
11534   {
11535     printf(":::");
11536     for (i = 0; i < MAX_PLAYERS; i++)
11537       printf(" %d, ", stored_player[i].effective_action);
11538     printf("\n");
11539   }
11540 #endif
11541 #endif
11542
11543   for (i = 0; i < MAX_PLAYERS; i++)
11544   {
11545     // allow engine snapshot in case of changed movement attempt
11546     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11547         (stored_player[i].effective_action & KEY_MOTION))
11548       game.snapshot.changed_action = TRUE;
11549
11550     // allow engine snapshot in case of snapping/dropping attempt
11551     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11552         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11553       game.snapshot.changed_action = TRUE;
11554
11555     game.snapshot.last_action[i] = stored_player[i].effective_action;
11556   }
11557
11558   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11559   {
11560     GameActions_EM_Main();
11561   }
11562   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11563   {
11564     GameActions_SP_Main();
11565   }
11566   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11567   {
11568     GameActions_MM_Main();
11569   }
11570   else
11571   {
11572     GameActions_RND_Main();
11573   }
11574
11575   BlitScreenToBitmap(backbuffer);
11576
11577   CheckLevelSolved();
11578   CheckLevelTime();
11579
11580   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11581
11582   if (global.show_frames_per_second)
11583   {
11584     static unsigned int fps_counter = 0;
11585     static int fps_frames = 0;
11586     unsigned int fps_delay_ms = Counter() - fps_counter;
11587
11588     fps_frames++;
11589
11590     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11591     {
11592       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11593
11594       fps_frames = 0;
11595       fps_counter = Counter();
11596
11597       // always draw FPS to screen after FPS value was updated
11598       redraw_mask |= REDRAW_FPS;
11599     }
11600
11601     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11602     if (GetDrawDeactivationMask() == REDRAW_NONE)
11603       redraw_mask |= REDRAW_FPS;
11604   }
11605 }
11606
11607 static void GameActions_CheckSaveEngineSnapshot(void)
11608 {
11609   if (!game.snapshot.save_snapshot)
11610     return;
11611
11612   // clear flag for saving snapshot _before_ saving snapshot
11613   game.snapshot.save_snapshot = FALSE;
11614
11615   SaveEngineSnapshotToList();
11616 }
11617
11618 void GameActions(void)
11619 {
11620   GameActionsExt();
11621
11622   GameActions_CheckSaveEngineSnapshot();
11623 }
11624
11625 void GameActions_EM_Main(void)
11626 {
11627   byte effective_action[MAX_PLAYERS];
11628   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11629   int i;
11630
11631   for (i = 0; i < MAX_PLAYERS; i++)
11632     effective_action[i] = stored_player[i].effective_action;
11633
11634   GameActions_EM(effective_action, warp_mode);
11635 }
11636
11637 void GameActions_SP_Main(void)
11638 {
11639   byte effective_action[MAX_PLAYERS];
11640   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11641   int i;
11642
11643   for (i = 0; i < MAX_PLAYERS; i++)
11644     effective_action[i] = stored_player[i].effective_action;
11645
11646   GameActions_SP(effective_action, warp_mode);
11647
11648   for (i = 0; i < MAX_PLAYERS; i++)
11649   {
11650     if (stored_player[i].force_dropping)
11651       stored_player[i].action |= KEY_BUTTON_DROP;
11652
11653     stored_player[i].force_dropping = FALSE;
11654   }
11655 }
11656
11657 void GameActions_MM_Main(void)
11658 {
11659   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11660
11661   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11662 }
11663
11664 void GameActions_RND_Main(void)
11665 {
11666   GameActions_RND();
11667 }
11668
11669 void GameActions_RND(void)
11670 {
11671   int magic_wall_x = 0, magic_wall_y = 0;
11672   int i, x, y, element, graphic, last_gfx_frame;
11673
11674   InitPlayfieldScanModeVars();
11675
11676   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11677   {
11678     SCAN_PLAYFIELD(x, y)
11679     {
11680       ChangeCount[x][y] = 0;
11681       ChangeEvent[x][y] = -1;
11682     }
11683   }
11684
11685   if (game.set_centered_player)
11686   {
11687     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11688
11689     // switching to "all players" only possible if all players fit to screen
11690     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11691     {
11692       game.centered_player_nr_next = game.centered_player_nr;
11693       game.set_centered_player = FALSE;
11694     }
11695
11696     // do not switch focus to non-existing (or non-active) player
11697     if (game.centered_player_nr_next >= 0 &&
11698         !stored_player[game.centered_player_nr_next].active)
11699     {
11700       game.centered_player_nr_next = game.centered_player_nr;
11701       game.set_centered_player = FALSE;
11702     }
11703   }
11704
11705   if (game.set_centered_player &&
11706       ScreenMovPos == 0)        // screen currently aligned at tile position
11707   {
11708     int sx, sy;
11709
11710     if (game.centered_player_nr_next == -1)
11711     {
11712       setScreenCenteredToAllPlayers(&sx, &sy);
11713     }
11714     else
11715     {
11716       sx = stored_player[game.centered_player_nr_next].jx;
11717       sy = stored_player[game.centered_player_nr_next].jy;
11718     }
11719
11720     game.centered_player_nr = game.centered_player_nr_next;
11721     game.set_centered_player = FALSE;
11722
11723     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11724     DrawGameDoorValues();
11725   }
11726
11727   for (i = 0; i < MAX_PLAYERS; i++)
11728   {
11729     int actual_player_action = stored_player[i].effective_action;
11730
11731 #if 1
11732     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11733        - rnd_equinox_tetrachloride 048
11734        - rnd_equinox_tetrachloride_ii 096
11735        - rnd_emanuel_schmieg 002
11736        - doctor_sloan_ww 001, 020
11737     */
11738     if (stored_player[i].MovPos == 0)
11739       CheckGravityMovement(&stored_player[i]);
11740 #endif
11741
11742     // overwrite programmed action with tape action
11743     if (stored_player[i].programmed_action)
11744       actual_player_action = stored_player[i].programmed_action;
11745
11746     PlayerActions(&stored_player[i], actual_player_action);
11747
11748     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11749   }
11750
11751   ScrollScreen(NULL, SCROLL_GO_ON);
11752
11753   /* for backwards compatibility, the following code emulates a fixed bug that
11754      occured when pushing elements (causing elements that just made their last
11755      pushing step to already (if possible) make their first falling step in the
11756      same game frame, which is bad); this code is also needed to use the famous
11757      "spring push bug" which is used in older levels and might be wanted to be
11758      used also in newer levels, but in this case the buggy pushing code is only
11759      affecting the "spring" element and no other elements */
11760
11761   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11762   {
11763     for (i = 0; i < MAX_PLAYERS; i++)
11764     {
11765       struct PlayerInfo *player = &stored_player[i];
11766       int x = player->jx;
11767       int y = player->jy;
11768
11769       if (player->active && player->is_pushing && player->is_moving &&
11770           IS_MOVING(x, y) &&
11771           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11772            Feld[x][y] == EL_SPRING))
11773       {
11774         ContinueMoving(x, y);
11775
11776         // continue moving after pushing (this is actually a bug)
11777         if (!IS_MOVING(x, y))
11778           Stop[x][y] = FALSE;
11779       }
11780     }
11781   }
11782
11783   SCAN_PLAYFIELD(x, y)
11784   {
11785     Last[x][y] = Feld[x][y];
11786
11787     ChangeCount[x][y] = 0;
11788     ChangeEvent[x][y] = -1;
11789
11790     // this must be handled before main playfield loop
11791     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11792     {
11793       MovDelay[x][y]--;
11794       if (MovDelay[x][y] <= 0)
11795         RemoveField(x, y);
11796     }
11797
11798     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11799     {
11800       MovDelay[x][y]--;
11801       if (MovDelay[x][y] <= 0)
11802       {
11803         RemoveField(x, y);
11804         TEST_DrawLevelField(x, y);
11805
11806         TestIfElementTouchesCustomElement(x, y);        // for empty space
11807       }
11808     }
11809
11810 #if DEBUG
11811     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11812     {
11813       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11814       printf("GameActions(): This should never happen!\n");
11815
11816       ChangePage[x][y] = -1;
11817     }
11818 #endif
11819
11820     Stop[x][y] = FALSE;
11821     if (WasJustMoving[x][y] > 0)
11822       WasJustMoving[x][y]--;
11823     if (WasJustFalling[x][y] > 0)
11824       WasJustFalling[x][y]--;
11825     if (CheckCollision[x][y] > 0)
11826       CheckCollision[x][y]--;
11827     if (CheckImpact[x][y] > 0)
11828       CheckImpact[x][y]--;
11829
11830     GfxFrame[x][y]++;
11831
11832     /* reset finished pushing action (not done in ContinueMoving() to allow
11833        continuous pushing animation for elements with zero push delay) */
11834     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11835     {
11836       ResetGfxAnimation(x, y);
11837       TEST_DrawLevelField(x, y);
11838     }
11839
11840 #if DEBUG
11841     if (IS_BLOCKED(x, y))
11842     {
11843       int oldx, oldy;
11844
11845       Blocked2Moving(x, y, &oldx, &oldy);
11846       if (!IS_MOVING(oldx, oldy))
11847       {
11848         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11849         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11850         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11851         printf("GameActions(): This should never happen!\n");
11852       }
11853     }
11854 #endif
11855   }
11856
11857   SCAN_PLAYFIELD(x, y)
11858   {
11859     element = Feld[x][y];
11860     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11861     last_gfx_frame = GfxFrame[x][y];
11862
11863     ResetGfxFrame(x, y);
11864
11865     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11866       DrawLevelGraphicAnimation(x, y, graphic);
11867
11868     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11869         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11870       ResetRandomAnimationValue(x, y);
11871
11872     SetRandomAnimationValue(x, y);
11873
11874     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11875
11876     if (IS_INACTIVE(element))
11877     {
11878       if (IS_ANIMATED(graphic))
11879         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11880
11881       continue;
11882     }
11883
11884     // this may take place after moving, so 'element' may have changed
11885     if (IS_CHANGING(x, y) &&
11886         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11887     {
11888       int page = element_info[element].event_page_nr[CE_DELAY];
11889
11890       HandleElementChange(x, y, page);
11891
11892       element = Feld[x][y];
11893       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11894     }
11895
11896     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11897     {
11898       StartMoving(x, y);
11899
11900       element = Feld[x][y];
11901       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11902
11903       if (IS_ANIMATED(graphic) &&
11904           !IS_MOVING(x, y) &&
11905           !Stop[x][y])
11906         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11907
11908       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11909         TEST_DrawTwinkleOnField(x, y);
11910     }
11911     else if (element == EL_ACID)
11912     {
11913       if (!Stop[x][y])
11914         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11915     }
11916     else if ((element == EL_EXIT_OPEN ||
11917               element == EL_EM_EXIT_OPEN ||
11918               element == EL_SP_EXIT_OPEN ||
11919               element == EL_STEEL_EXIT_OPEN ||
11920               element == EL_EM_STEEL_EXIT_OPEN ||
11921               element == EL_SP_TERMINAL ||
11922               element == EL_SP_TERMINAL_ACTIVE ||
11923               element == EL_EXTRA_TIME ||
11924               element == EL_SHIELD_NORMAL ||
11925               element == EL_SHIELD_DEADLY) &&
11926              IS_ANIMATED(graphic))
11927       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11928     else if (IS_MOVING(x, y))
11929       ContinueMoving(x, y);
11930     else if (IS_ACTIVE_BOMB(element))
11931       CheckDynamite(x, y);
11932     else if (element == EL_AMOEBA_GROWING)
11933       AmoebeWaechst(x, y);
11934     else if (element == EL_AMOEBA_SHRINKING)
11935       AmoebaDisappearing(x, y);
11936
11937 #if !USE_NEW_AMOEBA_CODE
11938     else if (IS_AMOEBALIVE(element))
11939       AmoebeAbleger(x, y);
11940 #endif
11941
11942     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11943       Life(x, y);
11944     else if (element == EL_EXIT_CLOSED)
11945       CheckExit(x, y);
11946     else if (element == EL_EM_EXIT_CLOSED)
11947       CheckExitEM(x, y);
11948     else if (element == EL_STEEL_EXIT_CLOSED)
11949       CheckExitSteel(x, y);
11950     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11951       CheckExitSteelEM(x, y);
11952     else if (element == EL_SP_EXIT_CLOSED)
11953       CheckExitSP(x, y);
11954     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11955              element == EL_EXPANDABLE_STEELWALL_GROWING)
11956       MauerWaechst(x, y);
11957     else if (element == EL_EXPANDABLE_WALL ||
11958              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11959              element == EL_EXPANDABLE_WALL_VERTICAL ||
11960              element == EL_EXPANDABLE_WALL_ANY ||
11961              element == EL_BD_EXPANDABLE_WALL)
11962       MauerAbleger(x, y);
11963     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11964              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11965              element == EL_EXPANDABLE_STEELWALL_ANY)
11966       MauerAblegerStahl(x, y);
11967     else if (element == EL_FLAMES)
11968       CheckForDragon(x, y);
11969     else if (element == EL_EXPLOSION)
11970       ; // drawing of correct explosion animation is handled separately
11971     else if (element == EL_ELEMENT_SNAPPING ||
11972              element == EL_DIAGONAL_SHRINKING ||
11973              element == EL_DIAGONAL_GROWING)
11974     {
11975       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11976
11977       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11978     }
11979     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11980       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11981
11982     if (IS_BELT_ACTIVE(element))
11983       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11984
11985     if (game.magic_wall_active)
11986     {
11987       int jx = local_player->jx, jy = local_player->jy;
11988
11989       // play the element sound at the position nearest to the player
11990       if ((element == EL_MAGIC_WALL_FULL ||
11991            element == EL_MAGIC_WALL_ACTIVE ||
11992            element == EL_MAGIC_WALL_EMPTYING ||
11993            element == EL_BD_MAGIC_WALL_FULL ||
11994            element == EL_BD_MAGIC_WALL_ACTIVE ||
11995            element == EL_BD_MAGIC_WALL_EMPTYING ||
11996            element == EL_DC_MAGIC_WALL_FULL ||
11997            element == EL_DC_MAGIC_WALL_ACTIVE ||
11998            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11999           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12000       {
12001         magic_wall_x = x;
12002         magic_wall_y = y;
12003       }
12004     }
12005   }
12006
12007 #if USE_NEW_AMOEBA_CODE
12008   // new experimental amoeba growth stuff
12009   if (!(FrameCounter % 8))
12010   {
12011     static unsigned int random = 1684108901;
12012
12013     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12014     {
12015       x = RND(lev_fieldx);
12016       y = RND(lev_fieldy);
12017       element = Feld[x][y];
12018
12019       if (!IS_PLAYER(x,y) &&
12020           (element == EL_EMPTY ||
12021            CAN_GROW_INTO(element) ||
12022            element == EL_QUICKSAND_EMPTY ||
12023            element == EL_QUICKSAND_FAST_EMPTY ||
12024            element == EL_ACID_SPLASH_LEFT ||
12025            element == EL_ACID_SPLASH_RIGHT))
12026       {
12027         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12028             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12029             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12030             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12031           Feld[x][y] = EL_AMOEBA_DROP;
12032       }
12033
12034       random = random * 129 + 1;
12035     }
12036   }
12037 #endif
12038
12039   game.explosions_delayed = FALSE;
12040
12041   SCAN_PLAYFIELD(x, y)
12042   {
12043     element = Feld[x][y];
12044
12045     if (ExplodeField[x][y])
12046       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12047     else if (element == EL_EXPLOSION)
12048       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12049
12050     ExplodeField[x][y] = EX_TYPE_NONE;
12051   }
12052
12053   game.explosions_delayed = TRUE;
12054
12055   if (game.magic_wall_active)
12056   {
12057     if (!(game.magic_wall_time_left % 4))
12058     {
12059       int element = Feld[magic_wall_x][magic_wall_y];
12060
12061       if (element == EL_BD_MAGIC_WALL_FULL ||
12062           element == EL_BD_MAGIC_WALL_ACTIVE ||
12063           element == EL_BD_MAGIC_WALL_EMPTYING)
12064         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12065       else if (element == EL_DC_MAGIC_WALL_FULL ||
12066                element == EL_DC_MAGIC_WALL_ACTIVE ||
12067                element == EL_DC_MAGIC_WALL_EMPTYING)
12068         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12069       else
12070         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12071     }
12072
12073     if (game.magic_wall_time_left > 0)
12074     {
12075       game.magic_wall_time_left--;
12076
12077       if (!game.magic_wall_time_left)
12078       {
12079         SCAN_PLAYFIELD(x, y)
12080         {
12081           element = Feld[x][y];
12082
12083           if (element == EL_MAGIC_WALL_ACTIVE ||
12084               element == EL_MAGIC_WALL_FULL)
12085           {
12086             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12087             TEST_DrawLevelField(x, y);
12088           }
12089           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12090                    element == EL_BD_MAGIC_WALL_FULL)
12091           {
12092             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12093             TEST_DrawLevelField(x, y);
12094           }
12095           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12096                    element == EL_DC_MAGIC_WALL_FULL)
12097           {
12098             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12099             TEST_DrawLevelField(x, y);
12100           }
12101         }
12102
12103         game.magic_wall_active = FALSE;
12104       }
12105     }
12106   }
12107
12108   if (game.light_time_left > 0)
12109   {
12110     game.light_time_left--;
12111
12112     if (game.light_time_left == 0)
12113       RedrawAllLightSwitchesAndInvisibleElements();
12114   }
12115
12116   if (game.timegate_time_left > 0)
12117   {
12118     game.timegate_time_left--;
12119
12120     if (game.timegate_time_left == 0)
12121       CloseAllOpenTimegates();
12122   }
12123
12124   if (game.lenses_time_left > 0)
12125   {
12126     game.lenses_time_left--;
12127
12128     if (game.lenses_time_left == 0)
12129       RedrawAllInvisibleElementsForLenses();
12130   }
12131
12132   if (game.magnify_time_left > 0)
12133   {
12134     game.magnify_time_left--;
12135
12136     if (game.magnify_time_left == 0)
12137       RedrawAllInvisibleElementsForMagnifier();
12138   }
12139
12140   for (i = 0; i < MAX_PLAYERS; i++)
12141   {
12142     struct PlayerInfo *player = &stored_player[i];
12143
12144     if (SHIELD_ON(player))
12145     {
12146       if (player->shield_deadly_time_left)
12147         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12148       else if (player->shield_normal_time_left)
12149         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12150     }
12151   }
12152
12153 #if USE_DELAYED_GFX_REDRAW
12154   SCAN_PLAYFIELD(x, y)
12155   {
12156     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12157     {
12158       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12159          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12160
12161       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12162         DrawLevelField(x, y);
12163
12164       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12165         DrawLevelFieldCrumbled(x, y);
12166
12167       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12168         DrawLevelFieldCrumbledNeighbours(x, y);
12169
12170       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12171         DrawTwinkleOnField(x, y);
12172     }
12173
12174     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12175   }
12176 #endif
12177
12178   DrawAllPlayers();
12179   PlayAllPlayersSound();
12180
12181   if (local_player->show_envelope != 0 && (!local_player->active ||
12182                                            local_player->MovPos == 0))
12183   {
12184     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12185
12186     local_player->show_envelope = 0;
12187   }
12188
12189   // use random number generator in every frame to make it less predictable
12190   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12191     RND(1);
12192 }
12193
12194 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12195 {
12196   int min_x = x, min_y = y, max_x = x, max_y = y;
12197   int i;
12198
12199   for (i = 0; i < MAX_PLAYERS; i++)
12200   {
12201     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12202
12203     if (!stored_player[i].active || &stored_player[i] == player)
12204       continue;
12205
12206     min_x = MIN(min_x, jx);
12207     min_y = MIN(min_y, jy);
12208     max_x = MAX(max_x, jx);
12209     max_y = MAX(max_y, jy);
12210   }
12211
12212   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12213 }
12214
12215 static boolean AllPlayersInVisibleScreen(void)
12216 {
12217   int i;
12218
12219   for (i = 0; i < MAX_PLAYERS; i++)
12220   {
12221     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12222
12223     if (!stored_player[i].active)
12224       continue;
12225
12226     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12227       return FALSE;
12228   }
12229
12230   return TRUE;
12231 }
12232
12233 void ScrollLevel(int dx, int dy)
12234 {
12235   int scroll_offset = 2 * TILEX_VAR;
12236   int x, y;
12237
12238   BlitBitmap(drawto_field, drawto_field,
12239              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12240              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12241              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12242              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12243              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12244              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12245
12246   if (dx != 0)
12247   {
12248     x = (dx == 1 ? BX1 : BX2);
12249     for (y = BY1; y <= BY2; y++)
12250       DrawScreenField(x, y);
12251   }
12252
12253   if (dy != 0)
12254   {
12255     y = (dy == 1 ? BY1 : BY2);
12256     for (x = BX1; x <= BX2; x++)
12257       DrawScreenField(x, y);
12258   }
12259
12260   redraw_mask |= REDRAW_FIELD;
12261 }
12262
12263 static boolean canFallDown(struct PlayerInfo *player)
12264 {
12265   int jx = player->jx, jy = player->jy;
12266
12267   return (IN_LEV_FIELD(jx, jy + 1) &&
12268           (IS_FREE(jx, jy + 1) ||
12269            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12270           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12271           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12272 }
12273
12274 static boolean canPassField(int x, int y, int move_dir)
12275 {
12276   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12277   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12278   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12279   int nextx = x + dx;
12280   int nexty = y + dy;
12281   int element = Feld[x][y];
12282
12283   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12284           !CAN_MOVE(element) &&
12285           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12286           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12287           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12288 }
12289
12290 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12291 {
12292   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12293   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12294   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12295   int newx = x + dx;
12296   int newy = y + dy;
12297
12298   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12299           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12300           (IS_DIGGABLE(Feld[newx][newy]) ||
12301            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12302            canPassField(newx, newy, move_dir)));
12303 }
12304
12305 static void CheckGravityMovement(struct PlayerInfo *player)
12306 {
12307   if (player->gravity && !player->programmed_action)
12308   {
12309     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12310     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12311     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12312     int jx = player->jx, jy = player->jy;
12313     boolean player_is_moving_to_valid_field =
12314       (!player_is_snapping &&
12315        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12316         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12317     boolean player_can_fall_down = canFallDown(player);
12318
12319     if (player_can_fall_down &&
12320         !player_is_moving_to_valid_field)
12321       player->programmed_action = MV_DOWN;
12322   }
12323 }
12324
12325 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12326 {
12327   return CheckGravityMovement(player);
12328
12329   if (player->gravity && !player->programmed_action)
12330   {
12331     int jx = player->jx, jy = player->jy;
12332     boolean field_under_player_is_free =
12333       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12334     boolean player_is_standing_on_valid_field =
12335       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12336        (IS_WALKABLE(Feld[jx][jy]) &&
12337         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12338
12339     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12340       player->programmed_action = MV_DOWN;
12341   }
12342 }
12343
12344 /*
12345   MovePlayerOneStep()
12346   -----------------------------------------------------------------------------
12347   dx, dy:               direction (non-diagonal) to try to move the player to
12348   real_dx, real_dy:     direction as read from input device (can be diagonal)
12349 */
12350
12351 boolean MovePlayerOneStep(struct PlayerInfo *player,
12352                           int dx, int dy, int real_dx, int real_dy)
12353 {
12354   int jx = player->jx, jy = player->jy;
12355   int new_jx = jx + dx, new_jy = jy + dy;
12356   int can_move;
12357   boolean player_can_move = !player->cannot_move;
12358
12359   if (!player->active || (!dx && !dy))
12360     return MP_NO_ACTION;
12361
12362   player->MovDir = (dx < 0 ? MV_LEFT :
12363                     dx > 0 ? MV_RIGHT :
12364                     dy < 0 ? MV_UP :
12365                     dy > 0 ? MV_DOWN :  MV_NONE);
12366
12367   if (!IN_LEV_FIELD(new_jx, new_jy))
12368     return MP_NO_ACTION;
12369
12370   if (!player_can_move)
12371   {
12372     if (player->MovPos == 0)
12373     {
12374       player->is_moving = FALSE;
12375       player->is_digging = FALSE;
12376       player->is_collecting = FALSE;
12377       player->is_snapping = FALSE;
12378       player->is_pushing = FALSE;
12379     }
12380   }
12381
12382   if (!network.enabled && game.centered_player_nr == -1 &&
12383       !AllPlayersInSight(player, new_jx, new_jy))
12384     return MP_NO_ACTION;
12385
12386   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12387   if (can_move != MP_MOVING)
12388     return can_move;
12389
12390   // check if DigField() has caused relocation of the player
12391   if (player->jx != jx || player->jy != jy)
12392     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12393
12394   StorePlayer[jx][jy] = 0;
12395   player->last_jx = jx;
12396   player->last_jy = jy;
12397   player->jx = new_jx;
12398   player->jy = new_jy;
12399   StorePlayer[new_jx][new_jy] = player->element_nr;
12400
12401   if (player->move_delay_value_next != -1)
12402   {
12403     player->move_delay_value = player->move_delay_value_next;
12404     player->move_delay_value_next = -1;
12405   }
12406
12407   player->MovPos =
12408     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12409
12410   player->step_counter++;
12411
12412   PlayerVisit[jx][jy] = FrameCounter;
12413
12414   player->is_moving = TRUE;
12415
12416 #if 1
12417   // should better be called in MovePlayer(), but this breaks some tapes
12418   ScrollPlayer(player, SCROLL_INIT);
12419 #endif
12420
12421   return MP_MOVING;
12422 }
12423
12424 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12425 {
12426   int jx = player->jx, jy = player->jy;
12427   int old_jx = jx, old_jy = jy;
12428   int moved = MP_NO_ACTION;
12429
12430   if (!player->active)
12431     return FALSE;
12432
12433   if (!dx && !dy)
12434   {
12435     if (player->MovPos == 0)
12436     {
12437       player->is_moving = FALSE;
12438       player->is_digging = FALSE;
12439       player->is_collecting = FALSE;
12440       player->is_snapping = FALSE;
12441       player->is_pushing = FALSE;
12442     }
12443
12444     return FALSE;
12445   }
12446
12447   if (player->move_delay > 0)
12448     return FALSE;
12449
12450   player->move_delay = -1;              // set to "uninitialized" value
12451
12452   // store if player is automatically moved to next field
12453   player->is_auto_moving = (player->programmed_action != MV_NONE);
12454
12455   // remove the last programmed player action
12456   player->programmed_action = 0;
12457
12458   if (player->MovPos)
12459   {
12460     // should only happen if pre-1.2 tape recordings are played
12461     // this is only for backward compatibility
12462
12463     int original_move_delay_value = player->move_delay_value;
12464
12465 #if DEBUG
12466     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12467            tape.counter);
12468 #endif
12469
12470     // scroll remaining steps with finest movement resolution
12471     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12472
12473     while (player->MovPos)
12474     {
12475       ScrollPlayer(player, SCROLL_GO_ON);
12476       ScrollScreen(NULL, SCROLL_GO_ON);
12477
12478       AdvanceFrameAndPlayerCounters(player->index_nr);
12479
12480       DrawAllPlayers();
12481       BackToFront_WithFrameDelay(0);
12482     }
12483
12484     player->move_delay_value = original_move_delay_value;
12485   }
12486
12487   player->is_active = FALSE;
12488
12489   if (player->last_move_dir & MV_HORIZONTAL)
12490   {
12491     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12492       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12493   }
12494   else
12495   {
12496     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12497       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12498   }
12499
12500   if (!moved && !player->is_active)
12501   {
12502     player->is_moving = FALSE;
12503     player->is_digging = FALSE;
12504     player->is_collecting = FALSE;
12505     player->is_snapping = FALSE;
12506     player->is_pushing = FALSE;
12507   }
12508
12509   jx = player->jx;
12510   jy = player->jy;
12511
12512   if (moved & MP_MOVING && !ScreenMovPos &&
12513       (player->index_nr == game.centered_player_nr ||
12514        game.centered_player_nr == -1))
12515   {
12516     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12517     int offset = game.scroll_delay_value;
12518
12519     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12520     {
12521       // actual player has left the screen -- scroll in that direction
12522       if (jx != old_jx)         // player has moved horizontally
12523         scroll_x += (jx - old_jx);
12524       else                      // player has moved vertically
12525         scroll_y += (jy - old_jy);
12526     }
12527     else
12528     {
12529       if (jx != old_jx)         // player has moved horizontally
12530       {
12531         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12532             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12533           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12534
12535         // don't scroll over playfield boundaries
12536         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12537           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12538
12539         // don't scroll more than one field at a time
12540         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12541
12542         // don't scroll against the player's moving direction
12543         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12544             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12545           scroll_x = old_scroll_x;
12546       }
12547       else                      // player has moved vertically
12548       {
12549         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12550             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12551           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12552
12553         // don't scroll over playfield boundaries
12554         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12555           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12556
12557         // don't scroll more than one field at a time
12558         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12559
12560         // don't scroll against the player's moving direction
12561         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12562             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12563           scroll_y = old_scroll_y;
12564       }
12565     }
12566
12567     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12568     {
12569       if (!network.enabled && game.centered_player_nr == -1 &&
12570           !AllPlayersInVisibleScreen())
12571       {
12572         scroll_x = old_scroll_x;
12573         scroll_y = old_scroll_y;
12574       }
12575       else
12576       {
12577         ScrollScreen(player, SCROLL_INIT);
12578         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12579       }
12580     }
12581   }
12582
12583   player->StepFrame = 0;
12584
12585   if (moved & MP_MOVING)
12586   {
12587     if (old_jx != jx && old_jy == jy)
12588       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12589     else if (old_jx == jx && old_jy != jy)
12590       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12591
12592     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12593
12594     player->last_move_dir = player->MovDir;
12595     player->is_moving = TRUE;
12596     player->is_snapping = FALSE;
12597     player->is_switching = FALSE;
12598     player->is_dropping = FALSE;
12599     player->is_dropping_pressed = FALSE;
12600     player->drop_pressed_delay = 0;
12601
12602 #if 0
12603     // should better be called here than above, but this breaks some tapes
12604     ScrollPlayer(player, SCROLL_INIT);
12605 #endif
12606   }
12607   else
12608   {
12609     CheckGravityMovementWhenNotMoving(player);
12610
12611     player->is_moving = FALSE;
12612
12613     /* at this point, the player is allowed to move, but cannot move right now
12614        (e.g. because of something blocking the way) -- ensure that the player
12615        is also allowed to move in the next frame (in old versions before 3.1.1,
12616        the player was forced to wait again for eight frames before next try) */
12617
12618     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12619       player->move_delay = 0;   // allow direct movement in the next frame
12620   }
12621
12622   if (player->move_delay == -1)         // not yet initialized by DigField()
12623     player->move_delay = player->move_delay_value;
12624
12625   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12626   {
12627     TestIfPlayerTouchesBadThing(jx, jy);
12628     TestIfPlayerTouchesCustomElement(jx, jy);
12629   }
12630
12631   if (!player->active)
12632     RemovePlayer(player);
12633
12634   return moved;
12635 }
12636
12637 void ScrollPlayer(struct PlayerInfo *player, int mode)
12638 {
12639   int jx = player->jx, jy = player->jy;
12640   int last_jx = player->last_jx, last_jy = player->last_jy;
12641   int move_stepsize = TILEX / player->move_delay_value;
12642
12643   if (!player->active)
12644     return;
12645
12646   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12647     return;
12648
12649   if (mode == SCROLL_INIT)
12650   {
12651     player->actual_frame_counter = FrameCounter;
12652     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12653
12654     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12655         Feld[last_jx][last_jy] == EL_EMPTY)
12656     {
12657       int last_field_block_delay = 0;   // start with no blocking at all
12658       int block_delay_adjustment = player->block_delay_adjustment;
12659
12660       // if player blocks last field, add delay for exactly one move
12661       if (player->block_last_field)
12662       {
12663         last_field_block_delay += player->move_delay_value;
12664
12665         // when blocking enabled, prevent moving up despite gravity
12666         if (player->gravity && player->MovDir == MV_UP)
12667           block_delay_adjustment = -1;
12668       }
12669
12670       // add block delay adjustment (also possible when not blocking)
12671       last_field_block_delay += block_delay_adjustment;
12672
12673       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12674       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12675     }
12676
12677     if (player->MovPos != 0)    // player has not yet reached destination
12678       return;
12679   }
12680   else if (!FrameReached(&player->actual_frame_counter, 1))
12681     return;
12682
12683   if (player->MovPos != 0)
12684   {
12685     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12686     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12687
12688     // before DrawPlayer() to draw correct player graphic for this case
12689     if (player->MovPos == 0)
12690       CheckGravityMovement(player);
12691   }
12692
12693   if (player->MovPos == 0)      // player reached destination field
12694   {
12695     if (player->move_delay_reset_counter > 0)
12696     {
12697       player->move_delay_reset_counter--;
12698
12699       if (player->move_delay_reset_counter == 0)
12700       {
12701         // continue with normal speed after quickly moving through gate
12702         HALVE_PLAYER_SPEED(player);
12703
12704         // be able to make the next move without delay
12705         player->move_delay = 0;
12706       }
12707     }
12708
12709     player->last_jx = jx;
12710     player->last_jy = jy;
12711
12712     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12713         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12714         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12715         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12716         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12717         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12718         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12719         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12720     {
12721       ExitPlayer(player);
12722
12723       if (game.players_still_needed == 0 &&
12724           (game.friends_still_needed == 0 ||
12725            IS_SP_ELEMENT(Feld[jx][jy])))
12726         LevelSolved();
12727     }
12728
12729     // this breaks one level: "machine", level 000
12730     {
12731       int move_direction = player->MovDir;
12732       int enter_side = MV_DIR_OPPOSITE(move_direction);
12733       int leave_side = move_direction;
12734       int old_jx = last_jx;
12735       int old_jy = last_jy;
12736       int old_element = Feld[old_jx][old_jy];
12737       int new_element = Feld[jx][jy];
12738
12739       if (IS_CUSTOM_ELEMENT(old_element))
12740         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12741                                    CE_LEFT_BY_PLAYER,
12742                                    player->index_bit, leave_side);
12743
12744       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12745                                           CE_PLAYER_LEAVES_X,
12746                                           player->index_bit, leave_side);
12747
12748       if (IS_CUSTOM_ELEMENT(new_element))
12749         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12750                                    player->index_bit, enter_side);
12751
12752       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12753                                           CE_PLAYER_ENTERS_X,
12754                                           player->index_bit, enter_side);
12755
12756       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12757                                         CE_MOVE_OF_X, move_direction);
12758     }
12759
12760     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12761     {
12762       TestIfPlayerTouchesBadThing(jx, jy);
12763       TestIfPlayerTouchesCustomElement(jx, jy);
12764
12765       /* needed because pushed element has not yet reached its destination,
12766          so it would trigger a change event at its previous field location */
12767       if (!player->is_pushing)
12768         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12769
12770       if (!player->active)
12771         RemovePlayer(player);
12772     }
12773
12774     if (!game.LevelSolved && level.use_step_counter)
12775     {
12776       int i;
12777
12778       TimePlayed++;
12779
12780       if (TimeLeft > 0)
12781       {
12782         TimeLeft--;
12783
12784         if (TimeLeft <= 10 && setup.time_limit)
12785           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12786
12787         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12788
12789         DisplayGameControlValues();
12790
12791         if (!TimeLeft && setup.time_limit)
12792           for (i = 0; i < MAX_PLAYERS; i++)
12793             KillPlayer(&stored_player[i]);
12794       }
12795       else if (game.no_time_limit && !game.all_players_gone)
12796       {
12797         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12798
12799         DisplayGameControlValues();
12800       }
12801     }
12802
12803     if (tape.single_step && tape.recording && !tape.pausing &&
12804         !player->programmed_action)
12805       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12806
12807     if (!player->programmed_action)
12808       CheckSaveEngineSnapshot(player);
12809   }
12810 }
12811
12812 void ScrollScreen(struct PlayerInfo *player, int mode)
12813 {
12814   static unsigned int screen_frame_counter = 0;
12815
12816   if (mode == SCROLL_INIT)
12817   {
12818     // set scrolling step size according to actual player's moving speed
12819     ScrollStepSize = TILEX / player->move_delay_value;
12820
12821     screen_frame_counter = FrameCounter;
12822     ScreenMovDir = player->MovDir;
12823     ScreenMovPos = player->MovPos;
12824     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12825     return;
12826   }
12827   else if (!FrameReached(&screen_frame_counter, 1))
12828     return;
12829
12830   if (ScreenMovPos)
12831   {
12832     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12833     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12834     redraw_mask |= REDRAW_FIELD;
12835   }
12836   else
12837     ScreenMovDir = MV_NONE;
12838 }
12839
12840 void TestIfPlayerTouchesCustomElement(int x, int y)
12841 {
12842   static int xy[4][2] =
12843   {
12844     { 0, -1 },
12845     { -1, 0 },
12846     { +1, 0 },
12847     { 0, +1 }
12848   };
12849   static int trigger_sides[4][2] =
12850   {
12851     // center side       border side
12852     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12853     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12854     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12855     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12856   };
12857   static int touch_dir[4] =
12858   {
12859     MV_LEFT | MV_RIGHT,
12860     MV_UP   | MV_DOWN,
12861     MV_UP   | MV_DOWN,
12862     MV_LEFT | MV_RIGHT
12863   };
12864   int center_element = Feld[x][y];      // should always be non-moving!
12865   int i;
12866
12867   for (i = 0; i < NUM_DIRECTIONS; i++)
12868   {
12869     int xx = x + xy[i][0];
12870     int yy = y + xy[i][1];
12871     int center_side = trigger_sides[i][0];
12872     int border_side = trigger_sides[i][1];
12873     int border_element;
12874
12875     if (!IN_LEV_FIELD(xx, yy))
12876       continue;
12877
12878     if (IS_PLAYER(x, y))                // player found at center element
12879     {
12880       struct PlayerInfo *player = PLAYERINFO(x, y);
12881
12882       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12883         border_element = Feld[xx][yy];          // may be moving!
12884       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12885         border_element = Feld[xx][yy];
12886       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12887         border_element = MovingOrBlocked2Element(xx, yy);
12888       else
12889         continue;               // center and border element do not touch
12890
12891       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12892                                  player->index_bit, border_side);
12893       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12894                                           CE_PLAYER_TOUCHES_X,
12895                                           player->index_bit, border_side);
12896
12897       {
12898         /* use player element that is initially defined in the level playfield,
12899            not the player element that corresponds to the runtime player number
12900            (example: a level that contains EL_PLAYER_3 as the only player would
12901            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12902         int player_element = PLAYERINFO(x, y)->initial_element;
12903
12904         CheckElementChangeBySide(xx, yy, border_element, player_element,
12905                                  CE_TOUCHING_X, border_side);
12906       }
12907     }
12908     else if (IS_PLAYER(xx, yy))         // player found at border element
12909     {
12910       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12911
12912       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12913       {
12914         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12915           continue;             // center and border element do not touch
12916       }
12917
12918       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12919                                  player->index_bit, center_side);
12920       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12921                                           CE_PLAYER_TOUCHES_X,
12922                                           player->index_bit, center_side);
12923
12924       {
12925         /* use player element that is initially defined in the level playfield,
12926            not the player element that corresponds to the runtime player number
12927            (example: a level that contains EL_PLAYER_3 as the only player would
12928            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12929         int player_element = PLAYERINFO(xx, yy)->initial_element;
12930
12931         CheckElementChangeBySide(x, y, center_element, player_element,
12932                                  CE_TOUCHING_X, center_side);
12933       }
12934
12935       break;
12936     }
12937   }
12938 }
12939
12940 void TestIfElementTouchesCustomElement(int x, int y)
12941 {
12942   static int xy[4][2] =
12943   {
12944     { 0, -1 },
12945     { -1, 0 },
12946     { +1, 0 },
12947     { 0, +1 }
12948   };
12949   static int trigger_sides[4][2] =
12950   {
12951     // center side      border side
12952     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12953     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12954     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12955     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12956   };
12957   static int touch_dir[4] =
12958   {
12959     MV_LEFT | MV_RIGHT,
12960     MV_UP   | MV_DOWN,
12961     MV_UP   | MV_DOWN,
12962     MV_LEFT | MV_RIGHT
12963   };
12964   boolean change_center_element = FALSE;
12965   int center_element = Feld[x][y];      // should always be non-moving!
12966   int border_element_old[NUM_DIRECTIONS];
12967   int i;
12968
12969   for (i = 0; i < NUM_DIRECTIONS; i++)
12970   {
12971     int xx = x + xy[i][0];
12972     int yy = y + xy[i][1];
12973     int border_element;
12974
12975     border_element_old[i] = -1;
12976
12977     if (!IN_LEV_FIELD(xx, yy))
12978       continue;
12979
12980     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12981       border_element = Feld[xx][yy];    // may be moving!
12982     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12983       border_element = Feld[xx][yy];
12984     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
12985       border_element = MovingOrBlocked2Element(xx, yy);
12986     else
12987       continue;                 // center and border element do not touch
12988
12989     border_element_old[i] = border_element;
12990   }
12991
12992   for (i = 0; i < NUM_DIRECTIONS; i++)
12993   {
12994     int xx = x + xy[i][0];
12995     int yy = y + xy[i][1];
12996     int center_side = trigger_sides[i][0];
12997     int border_element = border_element_old[i];
12998
12999     if (border_element == -1)
13000       continue;
13001
13002     // check for change of border element
13003     CheckElementChangeBySide(xx, yy, border_element, center_element,
13004                              CE_TOUCHING_X, center_side);
13005
13006     // (center element cannot be player, so we dont have to check this here)
13007   }
13008
13009   for (i = 0; i < NUM_DIRECTIONS; i++)
13010   {
13011     int xx = x + xy[i][0];
13012     int yy = y + xy[i][1];
13013     int border_side = trigger_sides[i][1];
13014     int border_element = border_element_old[i];
13015
13016     if (border_element == -1)
13017       continue;
13018
13019     // check for change of center element (but change it only once)
13020     if (!change_center_element)
13021       change_center_element =
13022         CheckElementChangeBySide(x, y, center_element, border_element,
13023                                  CE_TOUCHING_X, border_side);
13024
13025     if (IS_PLAYER(xx, yy))
13026     {
13027       /* use player element that is initially defined in the level playfield,
13028          not the player element that corresponds to the runtime player number
13029          (example: a level that contains EL_PLAYER_3 as the only player would
13030          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13031       int player_element = PLAYERINFO(xx, yy)->initial_element;
13032
13033       CheckElementChangeBySide(x, y, center_element, player_element,
13034                                CE_TOUCHING_X, border_side);
13035     }
13036   }
13037 }
13038
13039 void TestIfElementHitsCustomElement(int x, int y, int direction)
13040 {
13041   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13042   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13043   int hitx = x + dx, hity = y + dy;
13044   int hitting_element = Feld[x][y];
13045   int touched_element;
13046
13047   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13048     return;
13049
13050   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13051                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13052
13053   if (IN_LEV_FIELD(hitx, hity))
13054   {
13055     int opposite_direction = MV_DIR_OPPOSITE(direction);
13056     int hitting_side = direction;
13057     int touched_side = opposite_direction;
13058     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13059                           MovDir[hitx][hity] != direction ||
13060                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13061
13062     object_hit = TRUE;
13063
13064     if (object_hit)
13065     {
13066       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13067                                CE_HITTING_X, touched_side);
13068
13069       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13070                                CE_HIT_BY_X, hitting_side);
13071
13072       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13073                                CE_HIT_BY_SOMETHING, opposite_direction);
13074
13075       if (IS_PLAYER(hitx, hity))
13076       {
13077         /* use player element that is initially defined in the level playfield,
13078            not the player element that corresponds to the runtime player number
13079            (example: a level that contains EL_PLAYER_3 as the only player would
13080            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13081         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13082
13083         CheckElementChangeBySide(x, y, hitting_element, player_element,
13084                                  CE_HITTING_X, touched_side);
13085       }
13086     }
13087   }
13088
13089   // "hitting something" is also true when hitting the playfield border
13090   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13091                            CE_HITTING_SOMETHING, direction);
13092 }
13093
13094 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13095 {
13096   int i, kill_x = -1, kill_y = -1;
13097
13098   int bad_element = -1;
13099   static int test_xy[4][2] =
13100   {
13101     { 0, -1 },
13102     { -1, 0 },
13103     { +1, 0 },
13104     { 0, +1 }
13105   };
13106   static int test_dir[4] =
13107   {
13108     MV_UP,
13109     MV_LEFT,
13110     MV_RIGHT,
13111     MV_DOWN
13112   };
13113
13114   for (i = 0; i < NUM_DIRECTIONS; i++)
13115   {
13116     int test_x, test_y, test_move_dir, test_element;
13117
13118     test_x = good_x + test_xy[i][0];
13119     test_y = good_y + test_xy[i][1];
13120
13121     if (!IN_LEV_FIELD(test_x, test_y))
13122       continue;
13123
13124     test_move_dir =
13125       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13126
13127     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13128
13129     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13130        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13131     */
13132     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13133         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13134     {
13135       kill_x = test_x;
13136       kill_y = test_y;
13137       bad_element = test_element;
13138
13139       break;
13140     }
13141   }
13142
13143   if (kill_x != -1 || kill_y != -1)
13144   {
13145     if (IS_PLAYER(good_x, good_y))
13146     {
13147       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13148
13149       if (player->shield_deadly_time_left > 0 &&
13150           !IS_INDESTRUCTIBLE(bad_element))
13151         Bang(kill_x, kill_y);
13152       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13153         KillPlayer(player);
13154     }
13155     else
13156       Bang(good_x, good_y);
13157   }
13158 }
13159
13160 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13161 {
13162   int i, kill_x = -1, kill_y = -1;
13163   int bad_element = Feld[bad_x][bad_y];
13164   static int test_xy[4][2] =
13165   {
13166     { 0, -1 },
13167     { -1, 0 },
13168     { +1, 0 },
13169     { 0, +1 }
13170   };
13171   static int touch_dir[4] =
13172   {
13173     MV_LEFT | MV_RIGHT,
13174     MV_UP   | MV_DOWN,
13175     MV_UP   | MV_DOWN,
13176     MV_LEFT | MV_RIGHT
13177   };
13178   static int test_dir[4] =
13179   {
13180     MV_UP,
13181     MV_LEFT,
13182     MV_RIGHT,
13183     MV_DOWN
13184   };
13185
13186   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13187     return;
13188
13189   for (i = 0; i < NUM_DIRECTIONS; i++)
13190   {
13191     int test_x, test_y, test_move_dir, test_element;
13192
13193     test_x = bad_x + test_xy[i][0];
13194     test_y = bad_y + test_xy[i][1];
13195
13196     if (!IN_LEV_FIELD(test_x, test_y))
13197       continue;
13198
13199     test_move_dir =
13200       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13201
13202     test_element = Feld[test_x][test_y];
13203
13204     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13205        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13206     */
13207     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13208         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13209     {
13210       // good thing is player or penguin that does not move away
13211       if (IS_PLAYER(test_x, test_y))
13212       {
13213         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13214
13215         if (bad_element == EL_ROBOT && player->is_moving)
13216           continue;     // robot does not kill player if he is moving
13217
13218         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13219         {
13220           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13221             continue;           // center and border element do not touch
13222         }
13223
13224         kill_x = test_x;
13225         kill_y = test_y;
13226
13227         break;
13228       }
13229       else if (test_element == EL_PENGUIN)
13230       {
13231         kill_x = test_x;
13232         kill_y = test_y;
13233
13234         break;
13235       }
13236     }
13237   }
13238
13239   if (kill_x != -1 || kill_y != -1)
13240   {
13241     if (IS_PLAYER(kill_x, kill_y))
13242     {
13243       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13244
13245       if (player->shield_deadly_time_left > 0 &&
13246           !IS_INDESTRUCTIBLE(bad_element))
13247         Bang(bad_x, bad_y);
13248       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13249         KillPlayer(player);
13250     }
13251     else
13252       Bang(kill_x, kill_y);
13253   }
13254 }
13255
13256 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13257 {
13258   int bad_element = Feld[bad_x][bad_y];
13259   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13260   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13261   int test_x = bad_x + dx, test_y = bad_y + dy;
13262   int test_move_dir, test_element;
13263   int kill_x = -1, kill_y = -1;
13264
13265   if (!IN_LEV_FIELD(test_x, test_y))
13266     return;
13267
13268   test_move_dir =
13269     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13270
13271   test_element = Feld[test_x][test_y];
13272
13273   if (test_move_dir != bad_move_dir)
13274   {
13275     // good thing can be player or penguin that does not move away
13276     if (IS_PLAYER(test_x, test_y))
13277     {
13278       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13279
13280       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13281          player as being hit when he is moving towards the bad thing, because
13282          the "get hit by" condition would be lost after the player stops) */
13283       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13284         return;         // player moves away from bad thing
13285
13286       kill_x = test_x;
13287       kill_y = test_y;
13288     }
13289     else if (test_element == EL_PENGUIN)
13290     {
13291       kill_x = test_x;
13292       kill_y = test_y;
13293     }
13294   }
13295
13296   if (kill_x != -1 || kill_y != -1)
13297   {
13298     if (IS_PLAYER(kill_x, kill_y))
13299     {
13300       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13301
13302       if (player->shield_deadly_time_left > 0 &&
13303           !IS_INDESTRUCTIBLE(bad_element))
13304         Bang(bad_x, bad_y);
13305       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13306         KillPlayer(player);
13307     }
13308     else
13309       Bang(kill_x, kill_y);
13310   }
13311 }
13312
13313 void TestIfPlayerTouchesBadThing(int x, int y)
13314 {
13315   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13316 }
13317
13318 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13319 {
13320   TestIfGoodThingHitsBadThing(x, y, move_dir);
13321 }
13322
13323 void TestIfBadThingTouchesPlayer(int x, int y)
13324 {
13325   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13326 }
13327
13328 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13329 {
13330   TestIfBadThingHitsGoodThing(x, y, move_dir);
13331 }
13332
13333 void TestIfFriendTouchesBadThing(int x, int y)
13334 {
13335   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13336 }
13337
13338 void TestIfBadThingTouchesFriend(int x, int y)
13339 {
13340   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13341 }
13342
13343 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13344 {
13345   int i, kill_x = bad_x, kill_y = bad_y;
13346   static int xy[4][2] =
13347   {
13348     { 0, -1 },
13349     { -1, 0 },
13350     { +1, 0 },
13351     { 0, +1 }
13352   };
13353
13354   for (i = 0; i < NUM_DIRECTIONS; i++)
13355   {
13356     int x, y, element;
13357
13358     x = bad_x + xy[i][0];
13359     y = bad_y + xy[i][1];
13360     if (!IN_LEV_FIELD(x, y))
13361       continue;
13362
13363     element = Feld[x][y];
13364     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13365         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13366     {
13367       kill_x = x;
13368       kill_y = y;
13369       break;
13370     }
13371   }
13372
13373   if (kill_x != bad_x || kill_y != bad_y)
13374     Bang(bad_x, bad_y);
13375 }
13376
13377 void KillPlayer(struct PlayerInfo *player)
13378 {
13379   int jx = player->jx, jy = player->jy;
13380
13381   if (!player->active)
13382     return;
13383
13384 #if 0
13385   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13386          player->killed, player->active, player->reanimated);
13387 #endif
13388
13389   /* the following code was introduced to prevent an infinite loop when calling
13390      -> Bang()
13391      -> CheckTriggeredElementChangeExt()
13392      -> ExecuteCustomElementAction()
13393      -> KillPlayer()
13394      -> (infinitely repeating the above sequence of function calls)
13395      which occurs when killing the player while having a CE with the setting
13396      "kill player X when explosion of <player X>"; the solution using a new
13397      field "player->killed" was chosen for backwards compatibility, although
13398      clever use of the fields "player->active" etc. would probably also work */
13399 #if 1
13400   if (player->killed)
13401     return;
13402 #endif
13403
13404   player->killed = TRUE;
13405
13406   // remove accessible field at the player's position
13407   Feld[jx][jy] = EL_EMPTY;
13408
13409   // deactivate shield (else Bang()/Explode() would not work right)
13410   player->shield_normal_time_left = 0;
13411   player->shield_deadly_time_left = 0;
13412
13413 #if 0
13414   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13415          player->killed, player->active, player->reanimated);
13416 #endif
13417
13418   Bang(jx, jy);
13419
13420 #if 0
13421   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13422          player->killed, player->active, player->reanimated);
13423 #endif
13424
13425   if (player->reanimated)       // killed player may have been reanimated
13426     player->killed = player->reanimated = FALSE;
13427   else
13428     BuryPlayer(player);
13429 }
13430
13431 static void KillPlayerUnlessEnemyProtected(int x, int y)
13432 {
13433   if (!PLAYER_ENEMY_PROTECTED(x, y))
13434     KillPlayer(PLAYERINFO(x, y));
13435 }
13436
13437 static void KillPlayerUnlessExplosionProtected(int x, int y)
13438 {
13439   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13440     KillPlayer(PLAYERINFO(x, y));
13441 }
13442
13443 void BuryPlayer(struct PlayerInfo *player)
13444 {
13445   int jx = player->jx, jy = player->jy;
13446
13447   if (!player->active)
13448     return;
13449
13450   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13451   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13452
13453   RemovePlayer(player);
13454
13455   player->buried = TRUE;
13456
13457   if (game.all_players_gone)
13458     game.GameOver = TRUE;
13459 }
13460
13461 void RemovePlayer(struct PlayerInfo *player)
13462 {
13463   int jx = player->jx, jy = player->jy;
13464   int i, found = FALSE;
13465
13466   player->present = FALSE;
13467   player->active = FALSE;
13468
13469   // required for some CE actions (even if the player is not active anymore)
13470   player->MovPos = 0;
13471
13472   if (!ExplodeField[jx][jy])
13473     StorePlayer[jx][jy] = 0;
13474
13475   if (player->is_moving)
13476     TEST_DrawLevelField(player->last_jx, player->last_jy);
13477
13478   for (i = 0; i < MAX_PLAYERS; i++)
13479     if (stored_player[i].active)
13480       found = TRUE;
13481
13482   if (!found)
13483   {
13484     game.all_players_gone = TRUE;
13485     game.GameOver = TRUE;
13486   }
13487
13488   game.exit_x = game.robot_wheel_x = jx;
13489   game.exit_y = game.robot_wheel_y = jy;
13490 }
13491
13492 void ExitPlayer(struct PlayerInfo *player)
13493 {
13494   DrawPlayer(player);   // needed here only to cleanup last field
13495   RemovePlayer(player);
13496
13497   if (game.players_still_needed > 0)
13498     game.players_still_needed--;
13499 }
13500
13501 static void setFieldForSnapping(int x, int y, int element, int direction)
13502 {
13503   struct ElementInfo *ei = &element_info[element];
13504   int direction_bit = MV_DIR_TO_BIT(direction);
13505   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13506   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13507                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13508
13509   Feld[x][y] = EL_ELEMENT_SNAPPING;
13510   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13511
13512   ResetGfxAnimation(x, y);
13513
13514   GfxElement[x][y] = element;
13515   GfxAction[x][y] = action;
13516   GfxDir[x][y] = direction;
13517   GfxFrame[x][y] = -1;
13518 }
13519
13520 /*
13521   =============================================================================
13522   checkDiagonalPushing()
13523   -----------------------------------------------------------------------------
13524   check if diagonal input device direction results in pushing of object
13525   (by checking if the alternative direction is walkable, diggable, ...)
13526   =============================================================================
13527 */
13528
13529 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13530                                     int x, int y, int real_dx, int real_dy)
13531 {
13532   int jx, jy, dx, dy, xx, yy;
13533
13534   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13535     return TRUE;
13536
13537   // diagonal direction: check alternative direction
13538   jx = player->jx;
13539   jy = player->jy;
13540   dx = x - jx;
13541   dy = y - jy;
13542   xx = jx + (dx == 0 ? real_dx : 0);
13543   yy = jy + (dy == 0 ? real_dy : 0);
13544
13545   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13546 }
13547
13548 /*
13549   =============================================================================
13550   DigField()
13551   -----------------------------------------------------------------------------
13552   x, y:                 field next to player (non-diagonal) to try to dig to
13553   real_dx, real_dy:     direction as read from input device (can be diagonal)
13554   =============================================================================
13555 */
13556
13557 static int DigField(struct PlayerInfo *player,
13558                     int oldx, int oldy, int x, int y,
13559                     int real_dx, int real_dy, int mode)
13560 {
13561   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13562   boolean player_was_pushing = player->is_pushing;
13563   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13564   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13565   int jx = oldx, jy = oldy;
13566   int dx = x - jx, dy = y - jy;
13567   int nextx = x + dx, nexty = y + dy;
13568   int move_direction = (dx == -1 ? MV_LEFT  :
13569                         dx == +1 ? MV_RIGHT :
13570                         dy == -1 ? MV_UP    :
13571                         dy == +1 ? MV_DOWN  : MV_NONE);
13572   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13573   int dig_side = MV_DIR_OPPOSITE(move_direction);
13574   int old_element = Feld[jx][jy];
13575   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13576   int collect_count;
13577
13578   if (is_player)                // function can also be called by EL_PENGUIN
13579   {
13580     if (player->MovPos == 0)
13581     {
13582       player->is_digging = FALSE;
13583       player->is_collecting = FALSE;
13584     }
13585
13586     if (player->MovPos == 0)    // last pushing move finished
13587       player->is_pushing = FALSE;
13588
13589     if (mode == DF_NO_PUSH)     // player just stopped pushing
13590     {
13591       player->is_switching = FALSE;
13592       player->push_delay = -1;
13593
13594       return MP_NO_ACTION;
13595     }
13596   }
13597
13598   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13599     old_element = Back[jx][jy];
13600
13601   // in case of element dropped at player position, check background
13602   else if (Back[jx][jy] != EL_EMPTY &&
13603            game.engine_version >= VERSION_IDENT(2,2,0,0))
13604     old_element = Back[jx][jy];
13605
13606   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13607     return MP_NO_ACTION;        // field has no opening in this direction
13608
13609   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13610     return MP_NO_ACTION;        // field has no opening in this direction
13611
13612   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13613   {
13614     SplashAcid(x, y);
13615
13616     Feld[jx][jy] = player->artwork_element;
13617     InitMovingField(jx, jy, MV_DOWN);
13618     Store[jx][jy] = EL_ACID;
13619     ContinueMoving(jx, jy);
13620     BuryPlayer(player);
13621
13622     return MP_DONT_RUN_INTO;
13623   }
13624
13625   if (player_can_move && DONT_RUN_INTO(element))
13626   {
13627     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13628
13629     return MP_DONT_RUN_INTO;
13630   }
13631
13632   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13633     return MP_NO_ACTION;
13634
13635   collect_count = element_info[element].collect_count_initial;
13636
13637   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13638     return MP_NO_ACTION;
13639
13640   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13641     player_can_move = player_can_move_or_snap;
13642
13643   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13644       game.engine_version >= VERSION_IDENT(2,2,0,0))
13645   {
13646     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13647                                player->index_bit, dig_side);
13648     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13649                                         player->index_bit, dig_side);
13650
13651     if (element == EL_DC_LANDMINE)
13652       Bang(x, y);
13653
13654     if (Feld[x][y] != element)          // field changed by snapping
13655       return MP_ACTION;
13656
13657     return MP_NO_ACTION;
13658   }
13659
13660   if (player->gravity && is_player && !player->is_auto_moving &&
13661       canFallDown(player) && move_direction != MV_DOWN &&
13662       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13663     return MP_NO_ACTION;        // player cannot walk here due to gravity
13664
13665   if (player_can_move &&
13666       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13667   {
13668     int sound_element = SND_ELEMENT(element);
13669     int sound_action = ACTION_WALKING;
13670
13671     if (IS_RND_GATE(element))
13672     {
13673       if (!player->key[RND_GATE_NR(element)])
13674         return MP_NO_ACTION;
13675     }
13676     else if (IS_RND_GATE_GRAY(element))
13677     {
13678       if (!player->key[RND_GATE_GRAY_NR(element)])
13679         return MP_NO_ACTION;
13680     }
13681     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13682     {
13683       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13684         return MP_NO_ACTION;
13685     }
13686     else if (element == EL_EXIT_OPEN ||
13687              element == EL_EM_EXIT_OPEN ||
13688              element == EL_EM_EXIT_OPENING ||
13689              element == EL_STEEL_EXIT_OPEN ||
13690              element == EL_EM_STEEL_EXIT_OPEN ||
13691              element == EL_EM_STEEL_EXIT_OPENING ||
13692              element == EL_SP_EXIT_OPEN ||
13693              element == EL_SP_EXIT_OPENING)
13694     {
13695       sound_action = ACTION_PASSING;    // player is passing exit
13696     }
13697     else if (element == EL_EMPTY)
13698     {
13699       sound_action = ACTION_MOVING;             // nothing to walk on
13700     }
13701
13702     // play sound from background or player, whatever is available
13703     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13704       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13705     else
13706       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13707   }
13708   else if (player_can_move &&
13709            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13710   {
13711     if (!ACCESS_FROM(element, opposite_direction))
13712       return MP_NO_ACTION;      // field not accessible from this direction
13713
13714     if (CAN_MOVE(element))      // only fixed elements can be passed!
13715       return MP_NO_ACTION;
13716
13717     if (IS_EM_GATE(element))
13718     {
13719       if (!player->key[EM_GATE_NR(element)])
13720         return MP_NO_ACTION;
13721     }
13722     else if (IS_EM_GATE_GRAY(element))
13723     {
13724       if (!player->key[EM_GATE_GRAY_NR(element)])
13725         return MP_NO_ACTION;
13726     }
13727     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13728     {
13729       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13730         return MP_NO_ACTION;
13731     }
13732     else if (IS_EMC_GATE(element))
13733     {
13734       if (!player->key[EMC_GATE_NR(element)])
13735         return MP_NO_ACTION;
13736     }
13737     else if (IS_EMC_GATE_GRAY(element))
13738     {
13739       if (!player->key[EMC_GATE_GRAY_NR(element)])
13740         return MP_NO_ACTION;
13741     }
13742     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13743     {
13744       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13745         return MP_NO_ACTION;
13746     }
13747     else if (element == EL_DC_GATE_WHITE ||
13748              element == EL_DC_GATE_WHITE_GRAY ||
13749              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13750     {
13751       if (player->num_white_keys == 0)
13752         return MP_NO_ACTION;
13753
13754       player->num_white_keys--;
13755     }
13756     else if (IS_SP_PORT(element))
13757     {
13758       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13759           element == EL_SP_GRAVITY_PORT_RIGHT ||
13760           element == EL_SP_GRAVITY_PORT_UP ||
13761           element == EL_SP_GRAVITY_PORT_DOWN)
13762         player->gravity = !player->gravity;
13763       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13764                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13765                element == EL_SP_GRAVITY_ON_PORT_UP ||
13766                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13767         player->gravity = TRUE;
13768       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13769                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13770                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13771                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13772         player->gravity = FALSE;
13773     }
13774
13775     // automatically move to the next field with double speed
13776     player->programmed_action = move_direction;
13777
13778     if (player->move_delay_reset_counter == 0)
13779     {
13780       player->move_delay_reset_counter = 2;     // two double speed steps
13781
13782       DOUBLE_PLAYER_SPEED(player);
13783     }
13784
13785     PlayLevelSoundAction(x, y, ACTION_PASSING);
13786   }
13787   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13788   {
13789     RemoveField(x, y);
13790
13791     if (mode != DF_SNAP)
13792     {
13793       GfxElement[x][y] = GFX_ELEMENT(element);
13794       player->is_digging = TRUE;
13795     }
13796
13797     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13798
13799     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13800                                         player->index_bit, dig_side);
13801
13802     if (mode == DF_SNAP)
13803     {
13804       if (level.block_snap_field)
13805         setFieldForSnapping(x, y, element, move_direction);
13806       else
13807         TestIfElementTouchesCustomElement(x, y);        // for empty space
13808
13809       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13810                                           player->index_bit, dig_side);
13811     }
13812   }
13813   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13814   {
13815     RemoveField(x, y);
13816
13817     if (is_player && mode != DF_SNAP)
13818     {
13819       GfxElement[x][y] = element;
13820       player->is_collecting = TRUE;
13821     }
13822
13823     if (element == EL_SPEED_PILL)
13824     {
13825       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13826     }
13827     else if (element == EL_EXTRA_TIME && level.time > 0)
13828     {
13829       TimeLeft += level.extra_time;
13830
13831       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13832
13833       DisplayGameControlValues();
13834     }
13835     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13836     {
13837       player->shield_normal_time_left += level.shield_normal_time;
13838       if (element == EL_SHIELD_DEADLY)
13839         player->shield_deadly_time_left += level.shield_deadly_time;
13840     }
13841     else if (element == EL_DYNAMITE ||
13842              element == EL_EM_DYNAMITE ||
13843              element == EL_SP_DISK_RED)
13844     {
13845       if (player->inventory_size < MAX_INVENTORY_SIZE)
13846         player->inventory_element[player->inventory_size++] = element;
13847
13848       DrawGameDoorValues();
13849     }
13850     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13851     {
13852       player->dynabomb_count++;
13853       player->dynabombs_left++;
13854     }
13855     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13856     {
13857       player->dynabomb_size++;
13858     }
13859     else if (element == EL_DYNABOMB_INCREASE_POWER)
13860     {
13861       player->dynabomb_xl = TRUE;
13862     }
13863     else if (IS_KEY(element))
13864     {
13865       player->key[KEY_NR(element)] = TRUE;
13866
13867       DrawGameDoorValues();
13868     }
13869     else if (element == EL_DC_KEY_WHITE)
13870     {
13871       player->num_white_keys++;
13872
13873       // display white keys?
13874       // DrawGameDoorValues();
13875     }
13876     else if (IS_ENVELOPE(element))
13877     {
13878       player->show_envelope = element;
13879     }
13880     else if (element == EL_EMC_LENSES)
13881     {
13882       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13883
13884       RedrawAllInvisibleElementsForLenses();
13885     }
13886     else if (element == EL_EMC_MAGNIFIER)
13887     {
13888       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13889
13890       RedrawAllInvisibleElementsForMagnifier();
13891     }
13892     else if (IS_DROPPABLE(element) ||
13893              IS_THROWABLE(element))     // can be collected and dropped
13894     {
13895       int i;
13896
13897       if (collect_count == 0)
13898         player->inventory_infinite_element = element;
13899       else
13900         for (i = 0; i < collect_count; i++)
13901           if (player->inventory_size < MAX_INVENTORY_SIZE)
13902             player->inventory_element[player->inventory_size++] = element;
13903
13904       DrawGameDoorValues();
13905     }
13906     else if (collect_count > 0)
13907     {
13908       game.gems_still_needed -= collect_count;
13909       if (game.gems_still_needed < 0)
13910         game.gems_still_needed = 0;
13911
13912       game.snapshot.collected_item = TRUE;
13913
13914       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13915
13916       DisplayGameControlValues();
13917     }
13918
13919     RaiseScoreElement(element);
13920     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13921
13922     if (is_player)
13923       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13924                                           player->index_bit, dig_side);
13925
13926     if (mode == DF_SNAP)
13927     {
13928       if (level.block_snap_field)
13929         setFieldForSnapping(x, y, element, move_direction);
13930       else
13931         TestIfElementTouchesCustomElement(x, y);        // for empty space
13932
13933       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13934                                           player->index_bit, dig_side);
13935     }
13936   }
13937   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13938   {
13939     if (mode == DF_SNAP && element != EL_BD_ROCK)
13940       return MP_NO_ACTION;
13941
13942     if (CAN_FALL(element) && dy)
13943       return MP_NO_ACTION;
13944
13945     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13946         !(element == EL_SPRING && level.use_spring_bug))
13947       return MP_NO_ACTION;
13948
13949     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13950         ((move_direction & MV_VERTICAL &&
13951           ((element_info[element].move_pattern & MV_LEFT &&
13952             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13953            (element_info[element].move_pattern & MV_RIGHT &&
13954             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13955          (move_direction & MV_HORIZONTAL &&
13956           ((element_info[element].move_pattern & MV_UP &&
13957             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13958            (element_info[element].move_pattern & MV_DOWN &&
13959             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13960       return MP_NO_ACTION;
13961
13962     // do not push elements already moving away faster than player
13963     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13964         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13965       return MP_NO_ACTION;
13966
13967     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13968     {
13969       if (player->push_delay_value == -1 || !player_was_pushing)
13970         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13971     }
13972     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13973     {
13974       if (player->push_delay_value == -1)
13975         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13976     }
13977     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13978     {
13979       if (!player->is_pushing)
13980         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13981     }
13982
13983     player->is_pushing = TRUE;
13984     player->is_active = TRUE;
13985
13986     if (!(IN_LEV_FIELD(nextx, nexty) &&
13987           (IS_FREE(nextx, nexty) ||
13988            (IS_SB_ELEMENT(element) &&
13989             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13990            (IS_CUSTOM_ELEMENT(element) &&
13991             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13992       return MP_NO_ACTION;
13993
13994     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13995       return MP_NO_ACTION;
13996
13997     if (player->push_delay == -1)       // new pushing; restart delay
13998       player->push_delay = 0;
13999
14000     if (player->push_delay < player->push_delay_value &&
14001         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14002         element != EL_SPRING && element != EL_BALLOON)
14003     {
14004       // make sure that there is no move delay before next try to push
14005       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14006         player->move_delay = 0;
14007
14008       return MP_NO_ACTION;
14009     }
14010
14011     if (IS_CUSTOM_ELEMENT(element) &&
14012         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14013     {
14014       if (!DigFieldByCE(nextx, nexty, element))
14015         return MP_NO_ACTION;
14016     }
14017
14018     if (IS_SB_ELEMENT(element))
14019     {
14020       boolean sokoban_task_solved = FALSE;
14021
14022       if (element == EL_SOKOBAN_FIELD_FULL)
14023       {
14024         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14025
14026         IncrementSokobanFieldsNeeded();
14027         IncrementSokobanObjectsNeeded();
14028       }
14029
14030       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14031       {
14032         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14033
14034         DecrementSokobanFieldsNeeded();
14035         DecrementSokobanObjectsNeeded();
14036
14037         // sokoban object was pushed from empty field to sokoban field
14038         if (Back[x][y] == EL_EMPTY)
14039           sokoban_task_solved = TRUE;
14040       }
14041
14042       Feld[x][y] = EL_SOKOBAN_OBJECT;
14043
14044       if (Back[x][y] == Back[nextx][nexty])
14045         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14046       else if (Back[x][y] != 0)
14047         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14048                                     ACTION_EMPTYING);
14049       else
14050         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14051                                     ACTION_FILLING);
14052
14053       if (sokoban_task_solved &&
14054           game.sokoban_fields_still_needed == 0 &&
14055           game.sokoban_objects_still_needed == 0 &&
14056           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14057       {
14058         game.players_still_needed = 0;
14059
14060         LevelSolved();
14061
14062         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14063       }
14064     }
14065     else
14066       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14067
14068     InitMovingField(x, y, move_direction);
14069     GfxAction[x][y] = ACTION_PUSHING;
14070
14071     if (mode == DF_SNAP)
14072       ContinueMoving(x, y);
14073     else
14074       MovPos[x][y] = (dx != 0 ? dx : dy);
14075
14076     Pushed[x][y] = TRUE;
14077     Pushed[nextx][nexty] = TRUE;
14078
14079     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14080       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14081     else
14082       player->push_delay_value = -1;    // get new value later
14083
14084     // check for element change _after_ element has been pushed
14085     if (game.use_change_when_pushing_bug)
14086     {
14087       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14088                                  player->index_bit, dig_side);
14089       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14090                                           player->index_bit, dig_side);
14091     }
14092   }
14093   else if (IS_SWITCHABLE(element))
14094   {
14095     if (PLAYER_SWITCHING(player, x, y))
14096     {
14097       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14098                                           player->index_bit, dig_side);
14099
14100       return MP_ACTION;
14101     }
14102
14103     player->is_switching = TRUE;
14104     player->switch_x = x;
14105     player->switch_y = y;
14106
14107     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14108
14109     if (element == EL_ROBOT_WHEEL)
14110     {
14111       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14112
14113       game.robot_wheel_x = x;
14114       game.robot_wheel_y = y;
14115       game.robot_wheel_active = TRUE;
14116
14117       TEST_DrawLevelField(x, y);
14118     }
14119     else if (element == EL_SP_TERMINAL)
14120     {
14121       int xx, yy;
14122
14123       SCAN_PLAYFIELD(xx, yy)
14124       {
14125         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14126         {
14127           Bang(xx, yy);
14128         }
14129         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14130         {
14131           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14132
14133           ResetGfxAnimation(xx, yy);
14134           TEST_DrawLevelField(xx, yy);
14135         }
14136       }
14137     }
14138     else if (IS_BELT_SWITCH(element))
14139     {
14140       ToggleBeltSwitch(x, y);
14141     }
14142     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14143              element == EL_SWITCHGATE_SWITCH_DOWN ||
14144              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14145              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14146     {
14147       ToggleSwitchgateSwitch(x, y);
14148     }
14149     else if (element == EL_LIGHT_SWITCH ||
14150              element == EL_LIGHT_SWITCH_ACTIVE)
14151     {
14152       ToggleLightSwitch(x, y);
14153     }
14154     else if (element == EL_TIMEGATE_SWITCH ||
14155              element == EL_DC_TIMEGATE_SWITCH)
14156     {
14157       ActivateTimegateSwitch(x, y);
14158     }
14159     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14160              element == EL_BALLOON_SWITCH_RIGHT ||
14161              element == EL_BALLOON_SWITCH_UP    ||
14162              element == EL_BALLOON_SWITCH_DOWN  ||
14163              element == EL_BALLOON_SWITCH_NONE  ||
14164              element == EL_BALLOON_SWITCH_ANY)
14165     {
14166       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14167                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14168                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14169                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14170                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14171                              move_direction);
14172     }
14173     else if (element == EL_LAMP)
14174     {
14175       Feld[x][y] = EL_LAMP_ACTIVE;
14176       game.lights_still_needed--;
14177
14178       ResetGfxAnimation(x, y);
14179       TEST_DrawLevelField(x, y);
14180     }
14181     else if (element == EL_TIME_ORB_FULL)
14182     {
14183       Feld[x][y] = EL_TIME_ORB_EMPTY;
14184
14185       if (level.time > 0 || level.use_time_orb_bug)
14186       {
14187         TimeLeft += level.time_orb_time;
14188         game.no_time_limit = FALSE;
14189
14190         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14191
14192         DisplayGameControlValues();
14193       }
14194
14195       ResetGfxAnimation(x, y);
14196       TEST_DrawLevelField(x, y);
14197     }
14198     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14199              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14200     {
14201       int xx, yy;
14202
14203       game.ball_state = !game.ball_state;
14204
14205       SCAN_PLAYFIELD(xx, yy)
14206       {
14207         int e = Feld[xx][yy];
14208
14209         if (game.ball_state)
14210         {
14211           if (e == EL_EMC_MAGIC_BALL)
14212             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14213           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14214             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14215         }
14216         else
14217         {
14218           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14219             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14220           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14221             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14222         }
14223       }
14224     }
14225
14226     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14227                                         player->index_bit, dig_side);
14228
14229     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14230                                         player->index_bit, dig_side);
14231
14232     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14233                                         player->index_bit, dig_side);
14234
14235     return MP_ACTION;
14236   }
14237   else
14238   {
14239     if (!PLAYER_SWITCHING(player, x, y))
14240     {
14241       player->is_switching = TRUE;
14242       player->switch_x = x;
14243       player->switch_y = y;
14244
14245       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14246                                  player->index_bit, dig_side);
14247       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14248                                           player->index_bit, dig_side);
14249
14250       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14251                                  player->index_bit, dig_side);
14252       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14253                                           player->index_bit, dig_side);
14254     }
14255
14256     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14257                                player->index_bit, dig_side);
14258     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14259                                         player->index_bit, dig_side);
14260
14261     return MP_NO_ACTION;
14262   }
14263
14264   player->push_delay = -1;
14265
14266   if (is_player)                // function can also be called by EL_PENGUIN
14267   {
14268     if (Feld[x][y] != element)          // really digged/collected something
14269     {
14270       player->is_collecting = !player->is_digging;
14271       player->is_active = TRUE;
14272     }
14273   }
14274
14275   return MP_MOVING;
14276 }
14277
14278 static boolean DigFieldByCE(int x, int y, int digging_element)
14279 {
14280   int element = Feld[x][y];
14281
14282   if (!IS_FREE(x, y))
14283   {
14284     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14285                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14286                   ACTION_BREAKING);
14287
14288     // no element can dig solid indestructible elements
14289     if (IS_INDESTRUCTIBLE(element) &&
14290         !IS_DIGGABLE(element) &&
14291         !IS_COLLECTIBLE(element))
14292       return FALSE;
14293
14294     if (AmoebaNr[x][y] &&
14295         (element == EL_AMOEBA_FULL ||
14296          element == EL_BD_AMOEBA ||
14297          element == EL_AMOEBA_GROWING))
14298     {
14299       AmoebaCnt[AmoebaNr[x][y]]--;
14300       AmoebaCnt2[AmoebaNr[x][y]]--;
14301     }
14302
14303     if (IS_MOVING(x, y))
14304       RemoveMovingField(x, y);
14305     else
14306     {
14307       RemoveField(x, y);
14308       TEST_DrawLevelField(x, y);
14309     }
14310
14311     // if digged element was about to explode, prevent the explosion
14312     ExplodeField[x][y] = EX_TYPE_NONE;
14313
14314     PlayLevelSoundAction(x, y, action);
14315   }
14316
14317   Store[x][y] = EL_EMPTY;
14318
14319   // this makes it possible to leave the removed element again
14320   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14321     Store[x][y] = element;
14322
14323   return TRUE;
14324 }
14325
14326 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14327 {
14328   int jx = player->jx, jy = player->jy;
14329   int x = jx + dx, y = jy + dy;
14330   int snap_direction = (dx == -1 ? MV_LEFT  :
14331                         dx == +1 ? MV_RIGHT :
14332                         dy == -1 ? MV_UP    :
14333                         dy == +1 ? MV_DOWN  : MV_NONE);
14334   boolean can_continue_snapping = (level.continuous_snapping &&
14335                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14336
14337   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14338     return FALSE;
14339
14340   if (!player->active || !IN_LEV_FIELD(x, y))
14341     return FALSE;
14342
14343   if (dx && dy)
14344     return FALSE;
14345
14346   if (!dx && !dy)
14347   {
14348     if (player->MovPos == 0)
14349       player->is_pushing = FALSE;
14350
14351     player->is_snapping = FALSE;
14352
14353     if (player->MovPos == 0)
14354     {
14355       player->is_moving = FALSE;
14356       player->is_digging = FALSE;
14357       player->is_collecting = FALSE;
14358     }
14359
14360     return FALSE;
14361   }
14362
14363   // prevent snapping with already pressed snap key when not allowed
14364   if (player->is_snapping && !can_continue_snapping)
14365     return FALSE;
14366
14367   player->MovDir = snap_direction;
14368
14369   if (player->MovPos == 0)
14370   {
14371     player->is_moving = FALSE;
14372     player->is_digging = FALSE;
14373     player->is_collecting = FALSE;
14374   }
14375
14376   player->is_dropping = FALSE;
14377   player->is_dropping_pressed = FALSE;
14378   player->drop_pressed_delay = 0;
14379
14380   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14381     return FALSE;
14382
14383   player->is_snapping = TRUE;
14384   player->is_active = TRUE;
14385
14386   if (player->MovPos == 0)
14387   {
14388     player->is_moving = FALSE;
14389     player->is_digging = FALSE;
14390     player->is_collecting = FALSE;
14391   }
14392
14393   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14394     TEST_DrawLevelField(player->last_jx, player->last_jy);
14395
14396   TEST_DrawLevelField(x, y);
14397
14398   return TRUE;
14399 }
14400
14401 static boolean DropElement(struct PlayerInfo *player)
14402 {
14403   int old_element, new_element;
14404   int dropx = player->jx, dropy = player->jy;
14405   int drop_direction = player->MovDir;
14406   int drop_side = drop_direction;
14407   int drop_element = get_next_dropped_element(player);
14408
14409   /* do not drop an element on top of another element; when holding drop key
14410      pressed without moving, dropped element must move away before the next
14411      element can be dropped (this is especially important if the next element
14412      is dynamite, which can be placed on background for historical reasons) */
14413   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14414     return MP_ACTION;
14415
14416   if (IS_THROWABLE(drop_element))
14417   {
14418     dropx += GET_DX_FROM_DIR(drop_direction);
14419     dropy += GET_DY_FROM_DIR(drop_direction);
14420
14421     if (!IN_LEV_FIELD(dropx, dropy))
14422       return FALSE;
14423   }
14424
14425   old_element = Feld[dropx][dropy];     // old element at dropping position
14426   new_element = drop_element;           // default: no change when dropping
14427
14428   // check if player is active, not moving and ready to drop
14429   if (!player->active || player->MovPos || player->drop_delay > 0)
14430     return FALSE;
14431
14432   // check if player has anything that can be dropped
14433   if (new_element == EL_UNDEFINED)
14434     return FALSE;
14435
14436   // only set if player has anything that can be dropped
14437   player->is_dropping_pressed = TRUE;
14438
14439   // check if drop key was pressed long enough for EM style dynamite
14440   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14441     return FALSE;
14442
14443   // check if anything can be dropped at the current position
14444   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14445     return FALSE;
14446
14447   // collected custom elements can only be dropped on empty fields
14448   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14449     return FALSE;
14450
14451   if (old_element != EL_EMPTY)
14452     Back[dropx][dropy] = old_element;   // store old element on this field
14453
14454   ResetGfxAnimation(dropx, dropy);
14455   ResetRandomAnimationValue(dropx, dropy);
14456
14457   if (player->inventory_size > 0 ||
14458       player->inventory_infinite_element != EL_UNDEFINED)
14459   {
14460     if (player->inventory_size > 0)
14461     {
14462       player->inventory_size--;
14463
14464       DrawGameDoorValues();
14465
14466       if (new_element == EL_DYNAMITE)
14467         new_element = EL_DYNAMITE_ACTIVE;
14468       else if (new_element == EL_EM_DYNAMITE)
14469         new_element = EL_EM_DYNAMITE_ACTIVE;
14470       else if (new_element == EL_SP_DISK_RED)
14471         new_element = EL_SP_DISK_RED_ACTIVE;
14472     }
14473
14474     Feld[dropx][dropy] = new_element;
14475
14476     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14477       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14478                           el2img(Feld[dropx][dropy]), 0);
14479
14480     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14481
14482     // needed if previous element just changed to "empty" in the last frame
14483     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14484
14485     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14486                                player->index_bit, drop_side);
14487     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14488                                         CE_PLAYER_DROPS_X,
14489                                         player->index_bit, drop_side);
14490
14491     TestIfElementTouchesCustomElement(dropx, dropy);
14492   }
14493   else          // player is dropping a dyna bomb
14494   {
14495     player->dynabombs_left--;
14496
14497     Feld[dropx][dropy] = new_element;
14498
14499     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14500       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14501                           el2img(Feld[dropx][dropy]), 0);
14502
14503     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14504   }
14505
14506   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14507     InitField_WithBug1(dropx, dropy, FALSE);
14508
14509   new_element = Feld[dropx][dropy];     // element might have changed
14510
14511   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14512       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14513   {
14514     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14515       MovDir[dropx][dropy] = drop_direction;
14516
14517     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14518
14519     // do not cause impact style collision by dropping elements that can fall
14520     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14521   }
14522
14523   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14524   player->is_dropping = TRUE;
14525
14526   player->drop_pressed_delay = 0;
14527   player->is_dropping_pressed = FALSE;
14528
14529   player->drop_x = dropx;
14530   player->drop_y = dropy;
14531
14532   return TRUE;
14533 }
14534
14535 // ----------------------------------------------------------------------------
14536 // game sound playing functions
14537 // ----------------------------------------------------------------------------
14538
14539 static int *loop_sound_frame = NULL;
14540 static int *loop_sound_volume = NULL;
14541
14542 void InitPlayLevelSound(void)
14543 {
14544   int num_sounds = getSoundListSize();
14545
14546   checked_free(loop_sound_frame);
14547   checked_free(loop_sound_volume);
14548
14549   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14550   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14551 }
14552
14553 static void PlayLevelSound(int x, int y, int nr)
14554 {
14555   int sx = SCREENX(x), sy = SCREENY(y);
14556   int volume, stereo_position;
14557   int max_distance = 8;
14558   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14559
14560   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14561       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14562     return;
14563
14564   if (!IN_LEV_FIELD(x, y) ||
14565       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14566       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14567     return;
14568
14569   volume = SOUND_MAX_VOLUME;
14570
14571   if (!IN_SCR_FIELD(sx, sy))
14572   {
14573     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14574     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14575
14576     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14577   }
14578
14579   stereo_position = (SOUND_MAX_LEFT +
14580                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14581                      (SCR_FIELDX + 2 * max_distance));
14582
14583   if (IS_LOOP_SOUND(nr))
14584   {
14585     /* This assures that quieter loop sounds do not overwrite louder ones,
14586        while restarting sound volume comparison with each new game frame. */
14587
14588     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14589       return;
14590
14591     loop_sound_volume[nr] = volume;
14592     loop_sound_frame[nr] = FrameCounter;
14593   }
14594
14595   PlaySoundExt(nr, volume, stereo_position, type);
14596 }
14597
14598 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14599 {
14600   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14601                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14602                  y < LEVELY(BY1) ? LEVELY(BY1) :
14603                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14604                  sound_action);
14605 }
14606
14607 static void PlayLevelSoundAction(int x, int y, int action)
14608 {
14609   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14610 }
14611
14612 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14613 {
14614   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14615
14616   if (sound_effect != SND_UNDEFINED)
14617     PlayLevelSound(x, y, sound_effect);
14618 }
14619
14620 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14621                                               int action)
14622 {
14623   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14624
14625   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14626     PlayLevelSound(x, y, sound_effect);
14627 }
14628
14629 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14630 {
14631   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14632
14633   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14634     PlayLevelSound(x, y, sound_effect);
14635 }
14636
14637 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14638 {
14639   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14640
14641   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14642     StopSound(sound_effect);
14643 }
14644
14645 static int getLevelMusicNr(void)
14646 {
14647   if (levelset.music[level_nr] != MUS_UNDEFINED)
14648     return levelset.music[level_nr];            // from config file
14649   else
14650     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14651 }
14652
14653 static void FadeLevelSounds(void)
14654 {
14655   FadeSounds();
14656 }
14657
14658 static void FadeLevelMusic(void)
14659 {
14660   int music_nr = getLevelMusicNr();
14661   char *curr_music = getCurrentlyPlayingMusicFilename();
14662   char *next_music = getMusicInfoEntryFilename(music_nr);
14663
14664   if (!strEqual(curr_music, next_music))
14665     FadeMusic();
14666 }
14667
14668 void FadeLevelSoundsAndMusic(void)
14669 {
14670   FadeLevelSounds();
14671   FadeLevelMusic();
14672 }
14673
14674 static void PlayLevelMusic(void)
14675 {
14676   int music_nr = getLevelMusicNr();
14677   char *curr_music = getCurrentlyPlayingMusicFilename();
14678   char *next_music = getMusicInfoEntryFilename(music_nr);
14679
14680   if (!strEqual(curr_music, next_music))
14681     PlayMusicLoop(music_nr);
14682 }
14683
14684 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14685 {
14686   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14687   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14688   int x = xx - 1 - offset;
14689   int y = yy - 1 - offset;
14690
14691   switch (sample)
14692   {
14693     case SAMPLE_blank:
14694       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14695       break;
14696
14697     case SAMPLE_roll:
14698       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14699       break;
14700
14701     case SAMPLE_stone:
14702       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14703       break;
14704
14705     case SAMPLE_nut:
14706       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14707       break;
14708
14709     case SAMPLE_crack:
14710       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14711       break;
14712
14713     case SAMPLE_bug:
14714       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14715       break;
14716
14717     case SAMPLE_tank:
14718       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14719       break;
14720
14721     case SAMPLE_android_clone:
14722       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14723       break;
14724
14725     case SAMPLE_android_move:
14726       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14727       break;
14728
14729     case SAMPLE_spring:
14730       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14731       break;
14732
14733     case SAMPLE_slurp:
14734       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14735       break;
14736
14737     case SAMPLE_eater:
14738       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14739       break;
14740
14741     case SAMPLE_eater_eat:
14742       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14743       break;
14744
14745     case SAMPLE_alien:
14746       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14747       break;
14748
14749     case SAMPLE_collect:
14750       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14751       break;
14752
14753     case SAMPLE_diamond:
14754       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14755       break;
14756
14757     case SAMPLE_squash:
14758       // !!! CHECK THIS !!!
14759 #if 1
14760       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14761 #else
14762       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14763 #endif
14764       break;
14765
14766     case SAMPLE_wonderfall:
14767       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14768       break;
14769
14770     case SAMPLE_drip:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14772       break;
14773
14774     case SAMPLE_push:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14776       break;
14777
14778     case SAMPLE_dirt:
14779       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14780       break;
14781
14782     case SAMPLE_acid:
14783       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14784       break;
14785
14786     case SAMPLE_ball:
14787       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14788       break;
14789
14790     case SAMPLE_grow:
14791       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14792       break;
14793
14794     case SAMPLE_wonder:
14795       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14796       break;
14797
14798     case SAMPLE_door:
14799       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14800       break;
14801
14802     case SAMPLE_exit_open:
14803       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14804       break;
14805
14806     case SAMPLE_exit_leave:
14807       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14808       break;
14809
14810     case SAMPLE_dynamite:
14811       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14812       break;
14813
14814     case SAMPLE_tick:
14815       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14816       break;
14817
14818     case SAMPLE_press:
14819       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14820       break;
14821
14822     case SAMPLE_wheel:
14823       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14824       break;
14825
14826     case SAMPLE_boom:
14827       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14828       break;
14829
14830     case SAMPLE_die:
14831       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14832       break;
14833
14834     case SAMPLE_time:
14835       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14836       break;
14837
14838     default:
14839       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14840       break;
14841   }
14842 }
14843
14844 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14845 {
14846   int element = map_element_SP_to_RND(element_sp);
14847   int action = map_action_SP_to_RND(action_sp);
14848   int offset = (setup.sp_show_border_elements ? 0 : 1);
14849   int x = xx - offset;
14850   int y = yy - offset;
14851
14852   PlayLevelSoundElementAction(x, y, element, action);
14853 }
14854
14855 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14856 {
14857   int element = map_element_MM_to_RND(element_mm);
14858   int action = map_action_MM_to_RND(action_mm);
14859   int offset = 0;
14860   int x = xx - offset;
14861   int y = yy - offset;
14862
14863   if (!IS_MM_ELEMENT(element))
14864     element = EL_MM_DEFAULT;
14865
14866   PlayLevelSoundElementAction(x, y, element, action);
14867 }
14868
14869 void PlaySound_MM(int sound_mm)
14870 {
14871   int sound = map_sound_MM_to_RND(sound_mm);
14872
14873   if (sound == SND_UNDEFINED)
14874     return;
14875
14876   PlaySound(sound);
14877 }
14878
14879 void PlaySoundLoop_MM(int sound_mm)
14880 {
14881   int sound = map_sound_MM_to_RND(sound_mm);
14882
14883   if (sound == SND_UNDEFINED)
14884     return;
14885
14886   PlaySoundLoop(sound);
14887 }
14888
14889 void StopSound_MM(int sound_mm)
14890 {
14891   int sound = map_sound_MM_to_RND(sound_mm);
14892
14893   if (sound == SND_UNDEFINED)
14894     return;
14895
14896   StopSound(sound);
14897 }
14898
14899 void RaiseScore(int value)
14900 {
14901   game.score += value;
14902
14903   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14904
14905   DisplayGameControlValues();
14906 }
14907
14908 void RaiseScoreElement(int element)
14909 {
14910   switch (element)
14911   {
14912     case EL_EMERALD:
14913     case EL_BD_DIAMOND:
14914     case EL_EMERALD_YELLOW:
14915     case EL_EMERALD_RED:
14916     case EL_EMERALD_PURPLE:
14917     case EL_SP_INFOTRON:
14918       RaiseScore(level.score[SC_EMERALD]);
14919       break;
14920     case EL_DIAMOND:
14921       RaiseScore(level.score[SC_DIAMOND]);
14922       break;
14923     case EL_CRYSTAL:
14924       RaiseScore(level.score[SC_CRYSTAL]);
14925       break;
14926     case EL_PEARL:
14927       RaiseScore(level.score[SC_PEARL]);
14928       break;
14929     case EL_BUG:
14930     case EL_BD_BUTTERFLY:
14931     case EL_SP_ELECTRON:
14932       RaiseScore(level.score[SC_BUG]);
14933       break;
14934     case EL_SPACESHIP:
14935     case EL_BD_FIREFLY:
14936     case EL_SP_SNIKSNAK:
14937       RaiseScore(level.score[SC_SPACESHIP]);
14938       break;
14939     case EL_YAMYAM:
14940     case EL_DARK_YAMYAM:
14941       RaiseScore(level.score[SC_YAMYAM]);
14942       break;
14943     case EL_ROBOT:
14944       RaiseScore(level.score[SC_ROBOT]);
14945       break;
14946     case EL_PACMAN:
14947       RaiseScore(level.score[SC_PACMAN]);
14948       break;
14949     case EL_NUT:
14950       RaiseScore(level.score[SC_NUT]);
14951       break;
14952     case EL_DYNAMITE:
14953     case EL_EM_DYNAMITE:
14954     case EL_SP_DISK_RED:
14955     case EL_DYNABOMB_INCREASE_NUMBER:
14956     case EL_DYNABOMB_INCREASE_SIZE:
14957     case EL_DYNABOMB_INCREASE_POWER:
14958       RaiseScore(level.score[SC_DYNAMITE]);
14959       break;
14960     case EL_SHIELD_NORMAL:
14961     case EL_SHIELD_DEADLY:
14962       RaiseScore(level.score[SC_SHIELD]);
14963       break;
14964     case EL_EXTRA_TIME:
14965       RaiseScore(level.extra_time_score);
14966       break;
14967     case EL_KEY_1:
14968     case EL_KEY_2:
14969     case EL_KEY_3:
14970     case EL_KEY_4:
14971     case EL_EM_KEY_1:
14972     case EL_EM_KEY_2:
14973     case EL_EM_KEY_3:
14974     case EL_EM_KEY_4:
14975     case EL_EMC_KEY_5:
14976     case EL_EMC_KEY_6:
14977     case EL_EMC_KEY_7:
14978     case EL_EMC_KEY_8:
14979     case EL_DC_KEY_WHITE:
14980       RaiseScore(level.score[SC_KEY]);
14981       break;
14982     default:
14983       RaiseScore(element_info[element].collect_score);
14984       break;
14985   }
14986 }
14987
14988 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14989 {
14990   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14991   {
14992     // closing door required in case of envelope style request dialogs
14993     if (!skip_request)
14994     {
14995       // prevent short reactivation of overlay buttons while closing door
14996       SetOverlayActive(FALSE);
14997
14998       CloseDoor(DOOR_CLOSE_1);
14999     }
15000
15001     if (network.enabled)
15002       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15003     else
15004     {
15005       if (quick_quit)
15006         FadeSkipNextFadeIn();
15007
15008       SetGameStatus(GAME_MODE_MAIN);
15009
15010       DrawMainMenu();
15011     }
15012   }
15013   else          // continue playing the game
15014   {
15015     if (tape.playing && tape.deactivate_display)
15016       TapeDeactivateDisplayOff(TRUE);
15017
15018     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15019
15020     if (tape.playing && tape.deactivate_display)
15021       TapeDeactivateDisplayOn();
15022   }
15023 }
15024
15025 void RequestQuitGame(boolean ask_if_really_quit)
15026 {
15027   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15028   boolean skip_request = game.all_players_gone || quick_quit;
15029
15030   RequestQuitGameExt(skip_request, quick_quit,
15031                      "Do you really want to quit the game?");
15032 }
15033
15034 void RequestRestartGame(char *message)
15035 {
15036   game.restart_game_message = NULL;
15037
15038   boolean has_started_game = hasStartedNetworkGame();
15039   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15040
15041   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15042   {
15043     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15044   }
15045   else
15046   {
15047     SetGameStatus(GAME_MODE_MAIN);
15048
15049     DrawMainMenu();
15050   }
15051 }
15052
15053 void CheckGameOver(void)
15054 {
15055   static boolean last_game_over = FALSE;
15056   static int game_over_delay = 0;
15057   int game_over_delay_value = 50;
15058   boolean game_over = checkGameFailed();
15059
15060   // do not handle game over if request dialog is already active
15061   if (game.request_active)
15062     return;
15063
15064   // do not ask to play again if game was never actually played
15065   if (!game.GamePlayed)
15066     return;
15067
15068   if (!game_over)
15069   {
15070     last_game_over = FALSE;
15071     game_over_delay = game_over_delay_value;
15072
15073     return;
15074   }
15075
15076   if (game_over_delay > 0)
15077   {
15078     game_over_delay--;
15079
15080     return;
15081   }
15082
15083   if (last_game_over != game_over)
15084     game.restart_game_message = (hasStartedNetworkGame() ?
15085                                  "Game over! Play it again?" :
15086                                  "Game over!");
15087
15088   last_game_over = game_over;
15089 }
15090
15091 boolean checkGameSolved(void)
15092 {
15093   // set for all game engines if level was solved
15094   return game.LevelSolved_GameEnd;
15095 }
15096
15097 boolean checkGameFailed(void)
15098 {
15099   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15100     return (game_em.game_over && !game_em.level_solved);
15101   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15102     return (game_sp.game_over && !game_sp.level_solved);
15103   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15104     return (game_mm.game_over && !game_mm.level_solved);
15105   else                          // GAME_ENGINE_TYPE_RND
15106     return (game.GameOver && !game.LevelSolved);
15107 }
15108
15109 boolean checkGameEnded(void)
15110 {
15111   return (checkGameSolved() || checkGameFailed());
15112 }
15113
15114
15115 // ----------------------------------------------------------------------------
15116 // random generator functions
15117 // ----------------------------------------------------------------------------
15118
15119 unsigned int InitEngineRandom_RND(int seed)
15120 {
15121   game.num_random_calls = 0;
15122
15123   return InitEngineRandom(seed);
15124 }
15125
15126 unsigned int RND(int max)
15127 {
15128   if (max > 0)
15129   {
15130     game.num_random_calls++;
15131
15132     return GetEngineRandom(max);
15133   }
15134
15135   return 0;
15136 }
15137
15138
15139 // ----------------------------------------------------------------------------
15140 // game engine snapshot handling functions
15141 // ----------------------------------------------------------------------------
15142
15143 struct EngineSnapshotInfo
15144 {
15145   // runtime values for custom element collect score
15146   int collect_score[NUM_CUSTOM_ELEMENTS];
15147
15148   // runtime values for group element choice position
15149   int choice_pos[NUM_GROUP_ELEMENTS];
15150
15151   // runtime values for belt position animations
15152   int belt_graphic[4][NUM_BELT_PARTS];
15153   int belt_anim_mode[4][NUM_BELT_PARTS];
15154 };
15155
15156 static struct EngineSnapshotInfo engine_snapshot_rnd;
15157 static char *snapshot_level_identifier = NULL;
15158 static int snapshot_level_nr = -1;
15159
15160 static void SaveEngineSnapshotValues_RND(void)
15161 {
15162   static int belt_base_active_element[4] =
15163   {
15164     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15165     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15166     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15167     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15168   };
15169   int i, j;
15170
15171   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15172   {
15173     int element = EL_CUSTOM_START + i;
15174
15175     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15176   }
15177
15178   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15179   {
15180     int element = EL_GROUP_START + i;
15181
15182     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15183   }
15184
15185   for (i = 0; i < 4; i++)
15186   {
15187     for (j = 0; j < NUM_BELT_PARTS; j++)
15188     {
15189       int element = belt_base_active_element[i] + j;
15190       int graphic = el2img(element);
15191       int anim_mode = graphic_info[graphic].anim_mode;
15192
15193       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15194       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15195     }
15196   }
15197 }
15198
15199 static void LoadEngineSnapshotValues_RND(void)
15200 {
15201   unsigned int num_random_calls = game.num_random_calls;
15202   int i, j;
15203
15204   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15205   {
15206     int element = EL_CUSTOM_START + i;
15207
15208     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15209   }
15210
15211   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15212   {
15213     int element = EL_GROUP_START + i;
15214
15215     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15216   }
15217
15218   for (i = 0; i < 4; i++)
15219   {
15220     for (j = 0; j < NUM_BELT_PARTS; j++)
15221     {
15222       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15223       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15224
15225       graphic_info[graphic].anim_mode = anim_mode;
15226     }
15227   }
15228
15229   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15230   {
15231     InitRND(tape.random_seed);
15232     for (i = 0; i < num_random_calls; i++)
15233       RND(1);
15234   }
15235
15236   if (game.num_random_calls != num_random_calls)
15237   {
15238     Error(ERR_INFO, "number of random calls out of sync");
15239     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15240     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15241     Error(ERR_EXIT, "this should not happen -- please debug");
15242   }
15243 }
15244
15245 void FreeEngineSnapshotSingle(void)
15246 {
15247   FreeSnapshotSingle();
15248
15249   setString(&snapshot_level_identifier, NULL);
15250   snapshot_level_nr = -1;
15251 }
15252
15253 void FreeEngineSnapshotList(void)
15254 {
15255   FreeSnapshotList();
15256 }
15257
15258 static ListNode *SaveEngineSnapshotBuffers(void)
15259 {
15260   ListNode *buffers = NULL;
15261
15262   // copy some special values to a structure better suited for the snapshot
15263
15264   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15265     SaveEngineSnapshotValues_RND();
15266   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15267     SaveEngineSnapshotValues_EM();
15268   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15269     SaveEngineSnapshotValues_SP(&buffers);
15270   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15271     SaveEngineSnapshotValues_MM(&buffers);
15272
15273   // save values stored in special snapshot structure
15274
15275   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15276     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15277   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15278     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15279   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15280     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15281   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15282     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15283
15284   // save further RND engine values
15285
15286   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15287   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15288   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15289
15290   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15291   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15292   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15293   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15294   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15295
15296   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15297   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15298   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15299
15300   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15301
15302   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15303   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15304
15305   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15306   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15307   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15308   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15309   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15310   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15311   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15312   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15313   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15314   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15315   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15316   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15317   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15318   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15319   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15320   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15321   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15322   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15323
15324   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15325   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15326
15327   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15328   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15329   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15330
15331   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15332   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15333
15334   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15335   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15336   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15337   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15338   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15339
15340   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15341   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15342
15343 #if 0
15344   ListNode *node = engine_snapshot_list_rnd;
15345   int num_bytes = 0;
15346
15347   while (node != NULL)
15348   {
15349     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15350
15351     node = node->next;
15352   }
15353
15354   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15355 #endif
15356
15357   return buffers;
15358 }
15359
15360 void SaveEngineSnapshotSingle(void)
15361 {
15362   ListNode *buffers = SaveEngineSnapshotBuffers();
15363
15364   // finally save all snapshot buffers to single snapshot
15365   SaveSnapshotSingle(buffers);
15366
15367   // save level identification information
15368   setString(&snapshot_level_identifier, leveldir_current->identifier);
15369   snapshot_level_nr = level_nr;
15370 }
15371
15372 boolean CheckSaveEngineSnapshotToList(void)
15373 {
15374   boolean save_snapshot =
15375     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15376      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15377       game.snapshot.changed_action) ||
15378      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15379       game.snapshot.collected_item));
15380
15381   game.snapshot.changed_action = FALSE;
15382   game.snapshot.collected_item = FALSE;
15383   game.snapshot.save_snapshot = save_snapshot;
15384
15385   return save_snapshot;
15386 }
15387
15388 void SaveEngineSnapshotToList(void)
15389 {
15390   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15391       tape.quick_resume)
15392     return;
15393
15394   ListNode *buffers = SaveEngineSnapshotBuffers();
15395
15396   // finally save all snapshot buffers to snapshot list
15397   SaveSnapshotToList(buffers);
15398 }
15399
15400 void SaveEngineSnapshotToListInitial(void)
15401 {
15402   FreeEngineSnapshotList();
15403
15404   SaveEngineSnapshotToList();
15405 }
15406
15407 static void LoadEngineSnapshotValues(void)
15408 {
15409   // restore special values from snapshot structure
15410
15411   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15412     LoadEngineSnapshotValues_RND();
15413   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15414     LoadEngineSnapshotValues_EM();
15415   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15416     LoadEngineSnapshotValues_SP();
15417   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15418     LoadEngineSnapshotValues_MM();
15419 }
15420
15421 void LoadEngineSnapshotSingle(void)
15422 {
15423   LoadSnapshotSingle();
15424
15425   LoadEngineSnapshotValues();
15426 }
15427
15428 static void LoadEngineSnapshot_Undo(int steps)
15429 {
15430   LoadSnapshotFromList_Older(steps);
15431
15432   LoadEngineSnapshotValues();
15433 }
15434
15435 static void LoadEngineSnapshot_Redo(int steps)
15436 {
15437   LoadSnapshotFromList_Newer(steps);
15438
15439   LoadEngineSnapshotValues();
15440 }
15441
15442 boolean CheckEngineSnapshotSingle(void)
15443 {
15444   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15445           snapshot_level_nr == level_nr);
15446 }
15447
15448 boolean CheckEngineSnapshotList(void)
15449 {
15450   return CheckSnapshotList();
15451 }
15452
15453
15454 // ---------- new game button stuff -------------------------------------------
15455
15456 static struct
15457 {
15458   int graphic;
15459   struct XY *pos;
15460   int gadget_id;
15461   boolean *setup_value;
15462   boolean allowed_on_tape;
15463   char *infotext;
15464 } gamebutton_info[NUM_GAME_BUTTONS] =
15465 {
15466   {
15467     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15468     GAME_CTRL_ID_STOP,                          NULL,
15469     TRUE,                                       "stop game"
15470   },
15471   {
15472     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15473     GAME_CTRL_ID_PAUSE,                         NULL,
15474     TRUE,                                       "pause game"
15475   },
15476   {
15477     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15478     GAME_CTRL_ID_PLAY,                          NULL,
15479     TRUE,                                       "play game"
15480   },
15481   {
15482     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15483     GAME_CTRL_ID_UNDO,                          NULL,
15484     TRUE,                                       "undo step"
15485   },
15486   {
15487     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15488     GAME_CTRL_ID_REDO,                          NULL,
15489     TRUE,                                       "redo step"
15490   },
15491   {
15492     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15493     GAME_CTRL_ID_SAVE,                          NULL,
15494     TRUE,                                       "save game"
15495   },
15496   {
15497     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15498     GAME_CTRL_ID_PAUSE2,                        NULL,
15499     TRUE,                                       "pause game"
15500   },
15501   {
15502     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15503     GAME_CTRL_ID_LOAD,                          NULL,
15504     TRUE,                                       "load game"
15505   },
15506   {
15507     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15508     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15509     FALSE,                                      "stop game"
15510   },
15511   {
15512     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15513     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15514     FALSE,                                      "pause game"
15515   },
15516   {
15517     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15518     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15519     FALSE,                                      "play game"
15520   },
15521   {
15522     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15523     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15524     TRUE,                                       "background music on/off"
15525   },
15526   {
15527     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15528     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15529     TRUE,                                       "sound loops on/off"
15530   },
15531   {
15532     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15533     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15534     TRUE,                                       "normal sounds on/off"
15535   },
15536   {
15537     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15538     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15539     FALSE,                                      "background music on/off"
15540   },
15541   {
15542     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15543     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15544     FALSE,                                      "sound loops on/off"
15545   },
15546   {
15547     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15548     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15549     FALSE,                                      "normal sounds on/off"
15550   }
15551 };
15552
15553 void CreateGameButtons(void)
15554 {
15555   int i;
15556
15557   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15558   {
15559     int graphic = gamebutton_info[i].graphic;
15560     struct GraphicInfo *gfx = &graphic_info[graphic];
15561     struct XY *pos = gamebutton_info[i].pos;
15562     struct GadgetInfo *gi;
15563     int button_type;
15564     boolean checked;
15565     unsigned int event_mask;
15566     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15567     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15568     int base_x = (on_tape ? VX : DX);
15569     int base_y = (on_tape ? VY : DY);
15570     int gd_x   = gfx->src_x;
15571     int gd_y   = gfx->src_y;
15572     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15573     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15574     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15575     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15576     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15577     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15578     int id = i;
15579
15580     if (gfx->bitmap == NULL)
15581     {
15582       game_gadget[id] = NULL;
15583
15584       continue;
15585     }
15586
15587     if (id == GAME_CTRL_ID_STOP ||
15588         id == GAME_CTRL_ID_PANEL_STOP ||
15589         id == GAME_CTRL_ID_PLAY ||
15590         id == GAME_CTRL_ID_PANEL_PLAY ||
15591         id == GAME_CTRL_ID_SAVE ||
15592         id == GAME_CTRL_ID_LOAD)
15593     {
15594       button_type = GD_TYPE_NORMAL_BUTTON;
15595       checked = FALSE;
15596       event_mask = GD_EVENT_RELEASED;
15597     }
15598     else if (id == GAME_CTRL_ID_UNDO ||
15599              id == GAME_CTRL_ID_REDO)
15600     {
15601       button_type = GD_TYPE_NORMAL_BUTTON;
15602       checked = FALSE;
15603       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15604     }
15605     else
15606     {
15607       button_type = GD_TYPE_CHECK_BUTTON;
15608       checked = (gamebutton_info[i].setup_value != NULL ?
15609                  *gamebutton_info[i].setup_value : FALSE);
15610       event_mask = GD_EVENT_PRESSED;
15611     }
15612
15613     gi = CreateGadget(GDI_CUSTOM_ID, id,
15614                       GDI_IMAGE_ID, graphic,
15615                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15616                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15617                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15618                       GDI_WIDTH, gfx->width,
15619                       GDI_HEIGHT, gfx->height,
15620                       GDI_TYPE, button_type,
15621                       GDI_STATE, GD_BUTTON_UNPRESSED,
15622                       GDI_CHECKED, checked,
15623                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15624                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15625                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15626                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15627                       GDI_DIRECT_DRAW, FALSE,
15628                       GDI_EVENT_MASK, event_mask,
15629                       GDI_CALLBACK_ACTION, HandleGameButtons,
15630                       GDI_END);
15631
15632     if (gi == NULL)
15633       Error(ERR_EXIT, "cannot create gadget");
15634
15635     game_gadget[id] = gi;
15636   }
15637 }
15638
15639 void FreeGameButtons(void)
15640 {
15641   int i;
15642
15643   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15644     FreeGadget(game_gadget[i]);
15645 }
15646
15647 static void UnmapGameButtonsAtSamePosition(int id)
15648 {
15649   int i;
15650
15651   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15652     if (i != id &&
15653         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15654         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15655       UnmapGadget(game_gadget[i]);
15656 }
15657
15658 static void UnmapGameButtonsAtSamePosition_All(void)
15659 {
15660   if (setup.show_snapshot_buttons)
15661   {
15662     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15663     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15664     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15665   }
15666   else
15667   {
15668     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15669     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15670     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15671
15672     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15673     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15674     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15675   }
15676 }
15677
15678 static void MapGameButtonsAtSamePosition(int id)
15679 {
15680   int i;
15681
15682   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15683     if (i != id &&
15684         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15685         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15686       MapGadget(game_gadget[i]);
15687
15688   UnmapGameButtonsAtSamePosition_All();
15689 }
15690
15691 void MapUndoRedoButtons(void)
15692 {
15693   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15694   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15695
15696   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15697   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15698
15699   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15700 }
15701
15702 void UnmapUndoRedoButtons(void)
15703 {
15704   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15705   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15706
15707   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15708   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15709
15710   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15711 }
15712
15713 static void MapGameButtonsExt(boolean on_tape)
15714 {
15715   int i;
15716
15717   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15718     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15719         i != GAME_CTRL_ID_UNDO &&
15720         i != GAME_CTRL_ID_REDO)
15721       MapGadget(game_gadget[i]);
15722
15723   UnmapGameButtonsAtSamePosition_All();
15724
15725   RedrawGameButtons();
15726 }
15727
15728 static void UnmapGameButtonsExt(boolean on_tape)
15729 {
15730   int i;
15731
15732   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15733     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15734       UnmapGadget(game_gadget[i]);
15735 }
15736
15737 static void RedrawGameButtonsExt(boolean on_tape)
15738 {
15739   int i;
15740
15741   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15742     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15743       RedrawGadget(game_gadget[i]);
15744
15745   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15746   redraw_mask &= ~REDRAW_ALL;
15747 }
15748
15749 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15750 {
15751   if (gi == NULL)
15752     return;
15753
15754   gi->checked = state;
15755 }
15756
15757 static void RedrawSoundButtonGadget(int id)
15758 {
15759   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15760              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15761              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15762              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15763              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15764              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15765              id);
15766
15767   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15768   RedrawGadget(game_gadget[id2]);
15769 }
15770
15771 void MapGameButtons(void)
15772 {
15773   MapGameButtonsExt(FALSE);
15774 }
15775
15776 void UnmapGameButtons(void)
15777 {
15778   UnmapGameButtonsExt(FALSE);
15779 }
15780
15781 void RedrawGameButtons(void)
15782 {
15783   RedrawGameButtonsExt(FALSE);
15784 }
15785
15786 void MapGameButtonsOnTape(void)
15787 {
15788   MapGameButtonsExt(TRUE);
15789 }
15790
15791 void UnmapGameButtonsOnTape(void)
15792 {
15793   UnmapGameButtonsExt(TRUE);
15794 }
15795
15796 void RedrawGameButtonsOnTape(void)
15797 {
15798   RedrawGameButtonsExt(TRUE);
15799 }
15800
15801 static void GameUndoRedoExt(void)
15802 {
15803   ClearPlayerAction();
15804
15805   tape.pausing = TRUE;
15806
15807   RedrawPlayfield();
15808   UpdateAndDisplayGameControlValues();
15809
15810   DrawCompleteVideoDisplay();
15811   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15812   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15813   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15814
15815   BackToFront();
15816 }
15817
15818 static void GameUndo(int steps)
15819 {
15820   if (!CheckEngineSnapshotList())
15821     return;
15822
15823   LoadEngineSnapshot_Undo(steps);
15824
15825   GameUndoRedoExt();
15826 }
15827
15828 static void GameRedo(int steps)
15829 {
15830   if (!CheckEngineSnapshotList())
15831     return;
15832
15833   LoadEngineSnapshot_Redo(steps);
15834
15835   GameUndoRedoExt();
15836 }
15837
15838 static void HandleGameButtonsExt(int id, int button)
15839 {
15840   static boolean game_undo_executed = FALSE;
15841   int steps = BUTTON_STEPSIZE(button);
15842   boolean handle_game_buttons =
15843     (game_status == GAME_MODE_PLAYING ||
15844      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15845
15846   if (!handle_game_buttons)
15847     return;
15848
15849   switch (id)
15850   {
15851     case GAME_CTRL_ID_STOP:
15852     case GAME_CTRL_ID_PANEL_STOP:
15853       if (game_status == GAME_MODE_MAIN)
15854         break;
15855
15856       if (tape.playing)
15857         TapeStop();
15858       else
15859         RequestQuitGame(TRUE);
15860
15861       break;
15862
15863     case GAME_CTRL_ID_PAUSE:
15864     case GAME_CTRL_ID_PAUSE2:
15865     case GAME_CTRL_ID_PANEL_PAUSE:
15866       if (network.enabled && game_status == GAME_MODE_PLAYING)
15867       {
15868         if (tape.pausing)
15869           SendToServer_ContinuePlaying();
15870         else
15871           SendToServer_PausePlaying();
15872       }
15873       else
15874         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15875
15876       game_undo_executed = FALSE;
15877
15878       break;
15879
15880     case GAME_CTRL_ID_PLAY:
15881     case GAME_CTRL_ID_PANEL_PLAY:
15882       if (game_status == GAME_MODE_MAIN)
15883       {
15884         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15885       }
15886       else if (tape.pausing)
15887       {
15888         if (network.enabled)
15889           SendToServer_ContinuePlaying();
15890         else
15891           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15892       }
15893       break;
15894
15895     case GAME_CTRL_ID_UNDO:
15896       // Important: When using "save snapshot when collecting an item" mode,
15897       // load last (current) snapshot for first "undo" after pressing "pause"
15898       // (else the last-but-one snapshot would be loaded, because the snapshot
15899       // pointer already points to the last snapshot when pressing "pause",
15900       // which is fine for "every step/move" mode, but not for "every collect")
15901       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15902           !game_undo_executed)
15903         steps--;
15904
15905       game_undo_executed = TRUE;
15906
15907       GameUndo(steps);
15908       break;
15909
15910     case GAME_CTRL_ID_REDO:
15911       GameRedo(steps);
15912       break;
15913
15914     case GAME_CTRL_ID_SAVE:
15915       TapeQuickSave();
15916       break;
15917
15918     case GAME_CTRL_ID_LOAD:
15919       TapeQuickLoad();
15920       break;
15921
15922     case SOUND_CTRL_ID_MUSIC:
15923     case SOUND_CTRL_ID_PANEL_MUSIC:
15924       if (setup.sound_music)
15925       { 
15926         setup.sound_music = FALSE;
15927
15928         FadeMusic();
15929       }
15930       else if (audio.music_available)
15931       { 
15932         setup.sound = setup.sound_music = TRUE;
15933
15934         SetAudioMode(setup.sound);
15935
15936         if (game_status == GAME_MODE_PLAYING)
15937           PlayLevelMusic();
15938       }
15939
15940       RedrawSoundButtonGadget(id);
15941
15942       break;
15943
15944     case SOUND_CTRL_ID_LOOPS:
15945     case SOUND_CTRL_ID_PANEL_LOOPS:
15946       if (setup.sound_loops)
15947         setup.sound_loops = FALSE;
15948       else if (audio.loops_available)
15949       {
15950         setup.sound = setup.sound_loops = TRUE;
15951
15952         SetAudioMode(setup.sound);
15953       }
15954
15955       RedrawSoundButtonGadget(id);
15956
15957       break;
15958
15959     case SOUND_CTRL_ID_SIMPLE:
15960     case SOUND_CTRL_ID_PANEL_SIMPLE:
15961       if (setup.sound_simple)
15962         setup.sound_simple = FALSE;
15963       else if (audio.sound_available)
15964       {
15965         setup.sound = setup.sound_simple = TRUE;
15966
15967         SetAudioMode(setup.sound);
15968       }
15969
15970       RedrawSoundButtonGadget(id);
15971
15972       break;
15973
15974     default:
15975       break;
15976   }
15977 }
15978
15979 static void HandleGameButtons(struct GadgetInfo *gi)
15980 {
15981   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15982 }
15983
15984 void HandleSoundButtonKeys(Key key)
15985 {
15986   if (key == setup.shortcut.sound_simple)
15987     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15988   else if (key == setup.shortcut.sound_loops)
15989     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15990   else if (key == setup.shortcut.sound_music)
15991     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15992 }