d73efcfba0f89ab7495599b0eba0636e1777d1e9
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_active)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_active)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return game_em.ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return game_sp.red_disk_count;
2132   else
2133     return stored_player[player_nr].inventory_size;
2134 }
2135
2136 static void InitGameControlValues(void)
2137 {
2138   int i;
2139
2140   for (i = 0; game_panel_controls[i].nr != -1; i++)
2141   {
2142     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2143     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2144     struct TextPosInfo *pos = gpc->pos;
2145     int nr = gpc->nr;
2146     int type = gpc->type;
2147
2148     if (nr != i)
2149     {
2150       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2151       Error(ERR_EXIT, "this should not happen -- please debug");
2152     }
2153
2154     // force update of game controls after initialization
2155     gpc->value = gpc->last_value = -1;
2156     gpc->frame = gpc->last_frame = -1;
2157     gpc->gfx_frame = -1;
2158
2159     // determine panel value width for later calculation of alignment
2160     if (type == TYPE_INTEGER || type == TYPE_STRING)
2161     {
2162       pos->width = pos->size * getFontWidth(pos->font);
2163       pos->height = getFontHeight(pos->font);
2164     }
2165     else if (type == TYPE_ELEMENT)
2166     {
2167       pos->width = pos->size;
2168       pos->height = pos->size;
2169     }
2170
2171     // fill structure for game panel draw order
2172     gpo->nr = gpc->nr;
2173     gpo->sort_priority = pos->sort_priority;
2174   }
2175
2176   // sort game panel controls according to sort_priority and control number
2177   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2178         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2179 }
2180
2181 static void UpdatePlayfieldElementCount(void)
2182 {
2183   boolean use_element_count = FALSE;
2184   int i, j, x, y;
2185
2186   // first check if it is needed at all to calculate playfield element count
2187   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2188     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2189       use_element_count = TRUE;
2190
2191   if (!use_element_count)
2192     return;
2193
2194   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2195     element_info[i].element_count = 0;
2196
2197   SCAN_PLAYFIELD(x, y)
2198   {
2199     element_info[Feld[x][y]].element_count++;
2200   }
2201
2202   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2203     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2204       if (IS_IN_GROUP(j, i))
2205         element_info[EL_GROUP_START + i].element_count +=
2206           element_info[j].element_count;
2207 }
2208
2209 static void UpdateGameControlValues(void)
2210 {
2211   int i, k;
2212   int time = (game.LevelSolved ?
2213               game.LevelSolved_CountingTime :
2214               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2215               game_em.lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               game_sp.time_played :
2218               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219               game_mm.energy_left :
2220               game.no_time_limit ? TimePlayed : TimeLeft);
2221   int score = (game.LevelSolved ?
2222                game.LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                game_em.lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                game_sp.score :
2227                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2228                game_mm.score :
2229                game.score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               game_em.lev->gems_needed :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               game_sp.infotrons_still_needed :
2234               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235               game_mm.kettles_still_needed :
2236               game.gems_still_needed);
2237   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238                      game_em.lev->gems_needed > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      game_sp.infotrons_still_needed > 0 :
2241                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242                      game_mm.kettles_still_needed > 0 ||
2243                      game_mm.lights_still_needed > 0 :
2244                      game.gems_still_needed > 0 ||
2245                      game.sokoban_fields_still_needed > 0 ||
2246                      game.sokoban_objects_still_needed > 0 ||
2247                      game.lights_still_needed > 0);
2248   int health = (game.LevelSolved ?
2249                 game.LevelSolved_CountingHealth :
2250                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2251                 MM_HEALTH(game_mm.laser_overload_value) :
2252                 game.health);
2253
2254   UpdatePlayfieldElementCount();
2255
2256   // update game panel control values
2257
2258   // used instead of "level_nr" (for network games)
2259   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2260   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2261
2262   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2263   for (i = 0; i < MAX_NUM_KEYS; i++)
2264     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2265   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2266   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2267
2268   if (game.centered_player_nr == -1)
2269   {
2270     for (i = 0; i < MAX_PLAYERS; i++)
2271     {
2272       // only one player in Supaplex game engine
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274         break;
2275
2276       for (k = 0; k < MAX_NUM_KEYS; k++)
2277       {
2278         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2279         {
2280           if (game_em.ply[i]->keys & (1 << k))
2281             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282               get_key_element_from_nr(k);
2283         }
2284         else if (stored_player[i].key[k])
2285           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2286             get_key_element_from_nr(k);
2287       }
2288
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         getPlayerInventorySize(i);
2291
2292       if (stored_player[i].num_white_keys > 0)
2293         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2294           EL_DC_KEY_WHITE;
2295
2296       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2297         stored_player[i].num_white_keys;
2298     }
2299   }
2300   else
2301   {
2302     int player_nr = game.centered_player_nr;
2303
2304     for (k = 0; k < MAX_NUM_KEYS; k++)
2305     {
2306       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307       {
2308         if (game_em.ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843   // --------------------------------------------------------------------------
2844   // set flags for bugs and changes according to active game engine version
2845   // --------------------------------------------------------------------------
2846
2847   /*
2848     Summary of bugfix/change:
2849     Fixed handling for custom elements that change when pushed by the player.
2850
2851     Fixed/changed in version:
2852     3.1.0
2853
2854     Description:
2855     Before 3.1.0, custom elements that "change when pushing" changed directly
2856     after the player started pushing them (until then handled in "DigField()").
2857     Since 3.1.0, these custom elements are not changed until the "pushing"
2858     move of the element is finished (now handled in "ContinueMoving()").
2859
2860     Affected levels/tapes:
2861     The first condition is generally needed for all levels/tapes before version
2862     3.1.0, which might use the old behaviour before it was changed; known tapes
2863     that are affected are some tapes from the level set "Walpurgis Gardens" by
2864     Jamie Cullen.
2865     The second condition is an exception from the above case and is needed for
2866     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2867     above (including some development versions of 3.1.0), but before it was
2868     known that this change would break tapes like the above and was fixed in
2869     3.1.1, so that the changed behaviour was active although the engine version
2870     while recording maybe was before 3.1.0. There is at least one tape that is
2871     affected by this exception, which is the tape for the one-level set "Bug
2872     Machine" by Juergen Bonhagen.
2873   */
2874
2875   game.use_change_when_pushing_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2877      !(tape.playing &&
2878        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2879        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2880
2881   /*
2882     Summary of bugfix/change:
2883     Fixed handling for blocking the field the player leaves when moving.
2884
2885     Fixed/changed in version:
2886     3.1.1
2887
2888     Description:
2889     Before 3.1.1, when "block last field when moving" was enabled, the field
2890     the player is leaving when moving was blocked for the time of the move,
2891     and was directly unblocked afterwards. This resulted in the last field
2892     being blocked for exactly one less than the number of frames of one player
2893     move. Additionally, even when blocking was disabled, the last field was
2894     blocked for exactly one frame.
2895     Since 3.1.1, due to changes in player movement handling, the last field
2896     is not blocked at all when blocking is disabled. When blocking is enabled,
2897     the last field is blocked for exactly the number of frames of one player
2898     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2899     last field is blocked for exactly one more than the number of frames of
2900     one player move.
2901
2902     Affected levels/tapes:
2903     (!!! yet to be determined -- probably many !!!)
2904   */
2905
2906   game.use_block_last_field_bug =
2907     (game.engine_version < VERSION_IDENT(3,1,1,0));
2908
2909   /* various special flags and settings for native Emerald Mine game engine */
2910
2911   game_em.use_single_button =
2912     (game.engine_version > VERSION_IDENT(4,0,0,2));
2913
2914   game_em.use_snap_key_bug =
2915     (game.engine_version < VERSION_IDENT(4,0,1,0));
2916
2917   game_em.use_old_explosions =
2918     (game.engine_version < VERSION_IDENT(4,1,4,2));
2919
2920   // --------------------------------------------------------------------------
2921
2922   // set maximal allowed number of custom element changes per game frame
2923   game.max_num_changes_per_frame = 1;
2924
2925   // default scan direction: scan playfield from top/left to bottom/right
2926   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2927
2928   // dynamically adjust element properties according to game engine version
2929   InitElementPropertiesEngine(game.engine_version);
2930
2931 #if 0
2932   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2933   printf("          tape version == %06d [%s] [file: %06d]\n",
2934          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2935          tape.file_version);
2936   printf("       => game.engine_version == %06d\n", game.engine_version);
2937 #endif
2938
2939   // ---------- initialize player's initial move delay ------------------------
2940
2941   // dynamically adjust player properties according to level information
2942   for (i = 0; i < MAX_PLAYERS; i++)
2943     game.initial_move_delay_value[i] =
2944       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2945
2946   // dynamically adjust player properties according to game engine version
2947   for (i = 0; i < MAX_PLAYERS; i++)
2948     game.initial_move_delay[i] =
2949       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2950        game.initial_move_delay_value[i] : 0);
2951
2952   // ---------- initialize player's initial push delay ------------------------
2953
2954   // dynamically adjust player properties according to game engine version
2955   game.initial_push_delay_value =
2956     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2957
2958   // ---------- initialize changing elements ----------------------------------
2959
2960   // initialize changing elements information
2961   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2962   {
2963     struct ElementInfo *ei = &element_info[i];
2964
2965     // this pointer might have been changed in the level editor
2966     ei->change = &ei->change_page[0];
2967
2968     if (!IS_CUSTOM_ELEMENT(i))
2969     {
2970       ei->change->target_element = EL_EMPTY_SPACE;
2971       ei->change->delay_fixed = 0;
2972       ei->change->delay_random = 0;
2973       ei->change->delay_frames = 1;
2974     }
2975
2976     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2977     {
2978       ei->has_change_event[j] = FALSE;
2979
2980       ei->event_page_nr[j] = 0;
2981       ei->event_page[j] = &ei->change_page[0];
2982     }
2983   }
2984
2985   // add changing elements from pre-defined list
2986   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2987   {
2988     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2989     struct ElementInfo *ei = &element_info[ch_delay->element];
2990
2991     ei->change->target_element       = ch_delay->target_element;
2992     ei->change->delay_fixed          = ch_delay->change_delay;
2993
2994     ei->change->pre_change_function  = ch_delay->pre_change_function;
2995     ei->change->change_function      = ch_delay->change_function;
2996     ei->change->post_change_function = ch_delay->post_change_function;
2997
2998     ei->change->can_change = TRUE;
2999     ei->change->can_change_or_has_action = TRUE;
3000
3001     ei->has_change_event[CE_DELAY] = TRUE;
3002
3003     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3004     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3005   }
3006
3007   // ---------- initialize internal run-time variables ------------------------
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3012
3013     for (j = 0; j < ei->num_change_pages; j++)
3014     {
3015       ei->change_page[j].can_change_or_has_action =
3016         (ei->change_page[j].can_change |
3017          ei->change_page[j].has_action);
3018     }
3019   }
3020
3021   // add change events from custom element configuration
3022   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3023   {
3024     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3025
3026     for (j = 0; j < ei->num_change_pages; j++)
3027     {
3028       if (!ei->change_page[j].can_change_or_has_action)
3029         continue;
3030
3031       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3032       {
3033         // only add event page for the first page found with this event
3034         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3035         {
3036           ei->has_change_event[k] = TRUE;
3037
3038           ei->event_page_nr[k] = j;
3039           ei->event_page[k] = &ei->change_page[j];
3040         }
3041       }
3042     }
3043   }
3044
3045   // ---------- initialize reference elements in change conditions ------------
3046
3047   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3048   {
3049     int element = EL_CUSTOM_START + i;
3050     struct ElementInfo *ei = &element_info[element];
3051
3052     for (j = 0; j < ei->num_change_pages; j++)
3053     {
3054       int trigger_element = ei->change_page[j].initial_trigger_element;
3055
3056       if (trigger_element >= EL_PREV_CE_8 &&
3057           trigger_element <= EL_NEXT_CE_8)
3058         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3059
3060       ei->change_page[j].trigger_element = trigger_element;
3061     }
3062   }
3063
3064   // ---------- initialize run-time trigger player and element ----------------
3065
3066   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3067   {
3068     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3069
3070     for (j = 0; j < ei->num_change_pages; j++)
3071     {
3072       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3073       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3074       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3075       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3076       ei->change_page[j].actual_trigger_ce_value = 0;
3077       ei->change_page[j].actual_trigger_ce_score = 0;
3078     }
3079   }
3080
3081   // ---------- initialize trigger events -------------------------------------
3082
3083   // initialize trigger events information
3084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3085     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3086       trigger_events[i][j] = FALSE;
3087
3088   // add trigger events from element change event properties
3089   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3090   {
3091     struct ElementInfo *ei = &element_info[i];
3092
3093     for (j = 0; j < ei->num_change_pages; j++)
3094     {
3095       if (!ei->change_page[j].can_change_or_has_action)
3096         continue;
3097
3098       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3099       {
3100         int trigger_element = ei->change_page[j].trigger_element;
3101
3102         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3103         {
3104           if (ei->change_page[j].has_event[k])
3105           {
3106             if (IS_GROUP_ELEMENT(trigger_element))
3107             {
3108               struct ElementGroupInfo *group =
3109                 element_info[trigger_element].group;
3110
3111               for (l = 0; l < group->num_elements_resolved; l++)
3112                 trigger_events[group->element_resolved[l]][k] = TRUE;
3113             }
3114             else if (trigger_element == EL_ANY_ELEMENT)
3115               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3116                 trigger_events[l][k] = TRUE;
3117             else
3118               trigger_events[trigger_element][k] = TRUE;
3119           }
3120         }
3121       }
3122     }
3123   }
3124
3125   // ---------- initialize push delay -----------------------------------------
3126
3127   // initialize push delay values to default
3128   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3129   {
3130     if (!IS_CUSTOM_ELEMENT(i))
3131     {
3132       // set default push delay values (corrected since version 3.0.7-1)
3133       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3134       {
3135         element_info[i].push_delay_fixed = 2;
3136         element_info[i].push_delay_random = 8;
3137       }
3138       else
3139       {
3140         element_info[i].push_delay_fixed = 8;
3141         element_info[i].push_delay_random = 8;
3142       }
3143     }
3144   }
3145
3146   // set push delay value for certain elements from pre-defined list
3147   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3148   {
3149     int e = push_delay_list[i].element;
3150
3151     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3152     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3153   }
3154
3155   // set push delay value for Supaplex elements for newer engine versions
3156   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3157   {
3158     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     {
3160       if (IS_SP_ELEMENT(i))
3161       {
3162         // set SP push delay to just enough to push under a falling zonk
3163         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3164
3165         element_info[i].push_delay_fixed  = delay;
3166         element_info[i].push_delay_random = 0;
3167       }
3168     }
3169   }
3170
3171   // ---------- initialize move stepsize --------------------------------------
3172
3173   // initialize move stepsize values to default
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175     if (!IS_CUSTOM_ELEMENT(i))
3176       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3177
3178   // set move stepsize value for certain elements from pre-defined list
3179   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3180   {
3181     int e = move_stepsize_list[i].element;
3182
3183     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3184   }
3185
3186   // ---------- initialize collect score --------------------------------------
3187
3188   // initialize collect score values for custom elements from initial value
3189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3190     if (IS_CUSTOM_ELEMENT(i))
3191       element_info[i].collect_score = element_info[i].collect_score_initial;
3192
3193   // ---------- initialize collect count --------------------------------------
3194
3195   // initialize collect count values for non-custom elements
3196   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3197     if (!IS_CUSTOM_ELEMENT(i))
3198       element_info[i].collect_count_initial = 0;
3199
3200   // add collect count values for all elements from pre-defined list
3201   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3202     element_info[collect_count_list[i].element].collect_count_initial =
3203       collect_count_list[i].count;
3204
3205   // ---------- initialize access direction -----------------------------------
3206
3207   // initialize access direction values to default (access from every side)
3208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3209     if (!IS_CUSTOM_ELEMENT(i))
3210       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3211
3212   // set access direction value for certain elements from pre-defined list
3213   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3214     element_info[access_direction_list[i].element].access_direction =
3215       access_direction_list[i].direction;
3216
3217   // ---------- initialize explosion content ----------------------------------
3218   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3219   {
3220     if (IS_CUSTOM_ELEMENT(i))
3221       continue;
3222
3223     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3224     {
3225       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3226
3227       element_info[i].content.e[x][y] =
3228         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3229          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3230          i == EL_PLAYER_3 ? EL_EMERALD :
3231          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3232          i == EL_MOLE ? EL_EMERALD_RED :
3233          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3234          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3235          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3236          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3237          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3238          i == EL_WALL_EMERALD ? EL_EMERALD :
3239          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3240          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3241          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3242          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3243          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3244          i == EL_WALL_PEARL ? EL_PEARL :
3245          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3246          EL_EMPTY);
3247     }
3248   }
3249
3250   // ---------- initialize recursion detection --------------------------------
3251   recursion_loop_depth = 0;
3252   recursion_loop_detected = FALSE;
3253   recursion_loop_element = EL_UNDEFINED;
3254
3255   // ---------- initialize graphics engine ------------------------------------
3256   game.scroll_delay_value =
3257     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3258      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3259      !setup.forced_scroll_delay           ? 0 :
3260      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3261   game.scroll_delay_value =
3262     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3263
3264   // ---------- initialize game engine snapshots ------------------------------
3265   for (i = 0; i < MAX_PLAYERS; i++)
3266     game.snapshot.last_action[i] = 0;
3267   game.snapshot.changed_action = FALSE;
3268   game.snapshot.collected_item = FALSE;
3269   game.snapshot.mode =
3270     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3271      SNAPSHOT_MODE_EVERY_STEP :
3272      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3273      SNAPSHOT_MODE_EVERY_MOVE :
3274      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3275      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3276   game.snapshot.save_snapshot = FALSE;
3277
3278   // ---------- initialize level time for Supaplex engine ---------------------
3279   // Supaplex levels with time limit currently unsupported -- should be added
3280   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3281     level.time = 0;
3282 }
3283
3284 static int get_num_special_action(int element, int action_first,
3285                                   int action_last)
3286 {
3287   int num_special_action = 0;
3288   int i, j;
3289
3290   for (i = action_first; i <= action_last; i++)
3291   {
3292     boolean found = FALSE;
3293
3294     for (j = 0; j < NUM_DIRECTIONS; j++)
3295       if (el_act_dir2img(element, i, j) !=
3296           el_act_dir2img(element, ACTION_DEFAULT, j))
3297         found = TRUE;
3298
3299     if (found)
3300       num_special_action++;
3301     else
3302       break;
3303   }
3304
3305   return num_special_action;
3306 }
3307
3308
3309 // ============================================================================
3310 // InitGame()
3311 // ----------------------------------------------------------------------------
3312 // initialize and start new game
3313 // ============================================================================
3314
3315 #if DEBUG_INIT_PLAYER
3316 static void DebugPrintPlayerStatus(char *message)
3317 {
3318   int i;
3319
3320   if (!options.debug)
3321     return;
3322
3323   printf("%s:\n", message);
3324
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326   {
3327     struct PlayerInfo *player = &stored_player[i];
3328
3329     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3330            i + 1,
3331            player->present,
3332            player->connected,
3333            player->connected_locally,
3334            player->connected_network,
3335            player->active);
3336
3337     if (local_player == player)
3338       printf(" (local player)");
3339
3340     printf("\n");
3341   }
3342 }
3343 #endif
3344
3345 void InitGame(void)
3346 {
3347   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3348   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3349   int fade_mask = REDRAW_FIELD;
3350
3351   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3352   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3353   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3354   int initial_move_dir = MV_DOWN;
3355   int i, j, x, y;
3356
3357   // required here to update video display before fading (FIX THIS)
3358   DrawMaskedBorder(REDRAW_DOOR_2);
3359
3360   if (!game.restart_level)
3361     CloseDoor(DOOR_CLOSE_1);
3362
3363   SetGameStatus(GAME_MODE_PLAYING);
3364
3365   if (level_editor_test_game)
3366     FadeSkipNextFadeOut();
3367   else
3368     FadeSetEnterScreen();
3369
3370   if (CheckFadeAll())
3371     fade_mask = REDRAW_ALL;
3372
3373   FadeLevelSoundsAndMusic();
3374
3375   ExpireSoundLoops(TRUE);
3376
3377   FadeOut(fade_mask);
3378
3379   if (level_editor_test_game)
3380     FadeSkipNextFadeIn();
3381
3382   // needed if different viewport properties defined for playing
3383   ChangeViewportPropertiesIfNeeded();
3384
3385   ClearField();
3386
3387   DrawCompleteVideoDisplay();
3388
3389   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3390
3391   InitGameEngine();
3392   InitGameControlValues();
3393
3394   // don't play tapes over network
3395   network_playing = (network.enabled && !tape.playing);
3396
3397   for (i = 0; i < MAX_PLAYERS; i++)
3398   {
3399     struct PlayerInfo *player = &stored_player[i];
3400
3401     player->index_nr = i;
3402     player->index_bit = (1 << i);
3403     player->element_nr = EL_PLAYER_1 + i;
3404
3405     player->present = FALSE;
3406     player->active = FALSE;
3407     player->mapped = FALSE;
3408
3409     player->killed = FALSE;
3410     player->reanimated = FALSE;
3411     player->buried = FALSE;
3412
3413     player->action = 0;
3414     player->effective_action = 0;
3415     player->programmed_action = 0;
3416     player->snap_action = 0;
3417
3418     player->mouse_action.lx = 0;
3419     player->mouse_action.ly = 0;
3420     player->mouse_action.button = 0;
3421     player->mouse_action.button_hint = 0;
3422
3423     player->effective_mouse_action.lx = 0;
3424     player->effective_mouse_action.ly = 0;
3425     player->effective_mouse_action.button = 0;
3426     player->effective_mouse_action.button_hint = 0;
3427
3428     for (j = 0; j < MAX_NUM_KEYS; j++)
3429       player->key[j] = FALSE;
3430
3431     player->num_white_keys = 0;
3432
3433     player->dynabomb_count = 0;
3434     player->dynabomb_size = 1;
3435     player->dynabombs_left = 0;
3436     player->dynabomb_xl = FALSE;
3437
3438     player->MovDir = initial_move_dir;
3439     player->MovPos = 0;
3440     player->GfxPos = 0;
3441     player->GfxDir = initial_move_dir;
3442     player->GfxAction = ACTION_DEFAULT;
3443     player->Frame = 0;
3444     player->StepFrame = 0;
3445
3446     player->initial_element = player->element_nr;
3447     player->artwork_element =
3448       (level.use_artwork_element[i] ? level.artwork_element[i] :
3449        player->element_nr);
3450     player->use_murphy = FALSE;
3451
3452     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3453     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3454
3455     player->gravity = level.initial_player_gravity[i];
3456
3457     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3458
3459     player->actual_frame_counter = 0;
3460
3461     player->step_counter = 0;
3462
3463     player->last_move_dir = initial_move_dir;
3464
3465     player->is_active = FALSE;
3466
3467     player->is_waiting = FALSE;
3468     player->is_moving = FALSE;
3469     player->is_auto_moving = FALSE;
3470     player->is_digging = FALSE;
3471     player->is_snapping = FALSE;
3472     player->is_collecting = FALSE;
3473     player->is_pushing = FALSE;
3474     player->is_switching = FALSE;
3475     player->is_dropping = FALSE;
3476     player->is_dropping_pressed = FALSE;
3477
3478     player->is_bored = FALSE;
3479     player->is_sleeping = FALSE;
3480
3481     player->was_waiting = TRUE;
3482     player->was_moving = FALSE;
3483     player->was_snapping = FALSE;
3484     player->was_dropping = FALSE;
3485
3486     player->force_dropping = FALSE;
3487
3488     player->frame_counter_bored = -1;
3489     player->frame_counter_sleeping = -1;
3490
3491     player->anim_delay_counter = 0;
3492     player->post_delay_counter = 0;
3493
3494     player->dir_waiting = initial_move_dir;
3495     player->action_waiting = ACTION_DEFAULT;
3496     player->last_action_waiting = ACTION_DEFAULT;
3497     player->special_action_bored = ACTION_DEFAULT;
3498     player->special_action_sleeping = ACTION_DEFAULT;
3499
3500     player->switch_x = -1;
3501     player->switch_y = -1;
3502
3503     player->drop_x = -1;
3504     player->drop_y = -1;
3505
3506     player->show_envelope = 0;
3507
3508     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3509
3510     player->push_delay       = -1;      // initialized when pushing starts
3511     player->push_delay_value = game.initial_push_delay_value;
3512
3513     player->drop_delay = 0;
3514     player->drop_pressed_delay = 0;
3515
3516     player->last_jx = -1;
3517     player->last_jy = -1;
3518     player->jx = -1;
3519     player->jy = -1;
3520
3521     player->shield_normal_time_left = 0;
3522     player->shield_deadly_time_left = 0;
3523
3524     player->inventory_infinite_element = EL_UNDEFINED;
3525     player->inventory_size = 0;
3526
3527     if (level.use_initial_inventory[i])
3528     {
3529       for (j = 0; j < level.initial_inventory_size[i]; j++)
3530       {
3531         int element = level.initial_inventory_content[i][j];
3532         int collect_count = element_info[element].collect_count_initial;
3533         int k;
3534
3535         if (!IS_CUSTOM_ELEMENT(element))
3536           collect_count = 1;
3537
3538         if (collect_count == 0)
3539           player->inventory_infinite_element = element;
3540         else
3541           for (k = 0; k < collect_count; k++)
3542             if (player->inventory_size < MAX_INVENTORY_SIZE)
3543               player->inventory_element[player->inventory_size++] = element;
3544       }
3545     }
3546
3547     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3548     SnapField(player, 0, 0);
3549
3550     map_player_action[i] = i;
3551   }
3552
3553   network_player_action_received = FALSE;
3554
3555   // initial null action
3556   if (network_playing)
3557     SendToServer_MovePlayer(MV_NONE);
3558
3559   FrameCounter = 0;
3560   TimeFrames = 0;
3561   TimePlayed = 0;
3562   TimeLeft = level.time;
3563   TapeTime = 0;
3564
3565   ScreenMovDir = MV_NONE;
3566   ScreenMovPos = 0;
3567   ScreenGfxPos = 0;
3568
3569   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3570
3571   game.robot_wheel_x = -1;
3572   game.robot_wheel_y = -1;
3573
3574   game.exit_x = -1;
3575   game.exit_y = -1;
3576
3577   game.all_players_gone = FALSE;
3578
3579   game.LevelSolved = FALSE;
3580   game.GameOver = FALSE;
3581
3582   game.GamePlayed = !tape.playing;
3583
3584   game.LevelSolved_GameWon = FALSE;
3585   game.LevelSolved_GameEnd = FALSE;
3586   game.LevelSolved_SaveTape = FALSE;
3587   game.LevelSolved_SaveScore = FALSE;
3588
3589   game.LevelSolved_CountingTime = 0;
3590   game.LevelSolved_CountingScore = 0;
3591   game.LevelSolved_CountingHealth = 0;
3592
3593   game.panel.active = TRUE;
3594
3595   game.no_time_limit = (level.time == 0);
3596
3597   game.yamyam_content_nr = 0;
3598   game.robot_wheel_active = FALSE;
3599   game.magic_wall_active = FALSE;
3600   game.magic_wall_time_left = 0;
3601   game.light_time_left = 0;
3602   game.timegate_time_left = 0;
3603   game.switchgate_pos = 0;
3604   game.wind_direction = level.wind_direction_initial;
3605
3606   game.score = 0;
3607   game.score_final = 0;
3608
3609   game.health = MAX_HEALTH;
3610   game.health_final = MAX_HEALTH;
3611
3612   game.gems_still_needed = level.gems_needed;
3613   game.sokoban_fields_still_needed = 0;
3614   game.sokoban_objects_still_needed = 0;
3615   game.lights_still_needed = 0;
3616   game.players_still_needed = 0;
3617   game.friends_still_needed = 0;
3618
3619   game.lenses_time_left = 0;
3620   game.magnify_time_left = 0;
3621
3622   game.ball_active = level.ball_active_initial;
3623   game.ball_content_nr = 0;
3624
3625   game.explosions_delayed = TRUE;
3626
3627   game.envelope_active = FALSE;
3628
3629   for (i = 0; i < NUM_BELTS; i++)
3630   {
3631     game.belt_dir[i] = MV_NONE;
3632     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3633   }
3634
3635   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3636     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3637
3638 #if DEBUG_INIT_PLAYER
3639   DebugPrintPlayerStatus("Player status at level initialization");
3640 #endif
3641
3642   SCAN_PLAYFIELD(x, y)
3643   {
3644     Feld[x][y] = Last[x][y] = level.field[x][y];
3645     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3646     ChangeDelay[x][y] = 0;
3647     ChangePage[x][y] = -1;
3648     CustomValue[x][y] = 0;              // initialized in InitField()
3649     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3650     AmoebaNr[x][y] = 0;
3651     WasJustMoving[x][y] = 0;
3652     WasJustFalling[x][y] = 0;
3653     CheckCollision[x][y] = 0;
3654     CheckImpact[x][y] = 0;
3655     Stop[x][y] = FALSE;
3656     Pushed[x][y] = FALSE;
3657
3658     ChangeCount[x][y] = 0;
3659     ChangeEvent[x][y] = -1;
3660
3661     ExplodePhase[x][y] = 0;
3662     ExplodeDelay[x][y] = 0;
3663     ExplodeField[x][y] = EX_TYPE_NONE;
3664
3665     RunnerVisit[x][y] = 0;
3666     PlayerVisit[x][y] = 0;
3667
3668     GfxFrame[x][y] = 0;
3669     GfxRandom[x][y] = INIT_GFX_RANDOM();
3670     GfxElement[x][y] = EL_UNDEFINED;
3671     GfxAction[x][y] = ACTION_DEFAULT;
3672     GfxDir[x][y] = MV_NONE;
3673     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3674   }
3675
3676   SCAN_PLAYFIELD(x, y)
3677   {
3678     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3679       emulate_bd = FALSE;
3680     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3681       emulate_sb = FALSE;
3682     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3683       emulate_sp = FALSE;
3684
3685     InitField(x, y, TRUE);
3686
3687     ResetGfxAnimation(x, y);
3688   }
3689
3690   InitBeltMovement();
3691
3692   for (i = 0; i < MAX_PLAYERS; i++)
3693   {
3694     struct PlayerInfo *player = &stored_player[i];
3695
3696     // set number of special actions for bored and sleeping animation
3697     player->num_special_action_bored =
3698       get_num_special_action(player->artwork_element,
3699                              ACTION_BORING_1, ACTION_BORING_LAST);
3700     player->num_special_action_sleeping =
3701       get_num_special_action(player->artwork_element,
3702                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3703   }
3704
3705   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3706                     emulate_sb ? EMU_SOKOBAN :
3707                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3708
3709   // initialize type of slippery elements
3710   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3711   {
3712     if (!IS_CUSTOM_ELEMENT(i))
3713     {
3714       // default: elements slip down either to the left or right randomly
3715       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3716
3717       // SP style elements prefer to slip down on the left side
3718       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3719         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3720
3721       // BD style elements prefer to slip down on the left side
3722       if (game.emulation == EMU_BOULDERDASH)
3723         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3724     }
3725   }
3726
3727   // initialize explosion and ignition delay
3728   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3729   {
3730     if (!IS_CUSTOM_ELEMENT(i))
3731     {
3732       int num_phase = 8;
3733       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3734                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3735                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3736       int last_phase = (num_phase + 1) * delay;
3737       int half_phase = (num_phase / 2) * delay;
3738
3739       element_info[i].explosion_delay = last_phase - 1;
3740       element_info[i].ignition_delay = half_phase;
3741
3742       if (i == EL_BLACK_ORB)
3743         element_info[i].ignition_delay = 1;
3744     }
3745   }
3746
3747   // correct non-moving belts to start moving left
3748   for (i = 0; i < NUM_BELTS; i++)
3749     if (game.belt_dir[i] == MV_NONE)
3750       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3751
3752 #if USE_NEW_PLAYER_ASSIGNMENTS
3753   // use preferred player also in local single-player mode
3754   if (!network.enabled && !game.team_mode)
3755   {
3756     int new_index_nr = setup.network_player_nr;
3757
3758     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3759     {
3760       for (i = 0; i < MAX_PLAYERS; i++)
3761         stored_player[i].connected_locally = FALSE;
3762
3763       stored_player[new_index_nr].connected_locally = TRUE;
3764     }
3765   }
3766
3767   for (i = 0; i < MAX_PLAYERS; i++)
3768   {
3769     stored_player[i].connected = FALSE;
3770
3771     // in network game mode, the local player might not be the first player
3772     if (stored_player[i].connected_locally)
3773       local_player = &stored_player[i];
3774   }
3775
3776   if (!network.enabled)
3777     local_player->connected = TRUE;
3778
3779   if (tape.playing)
3780   {
3781     for (i = 0; i < MAX_PLAYERS; i++)
3782       stored_player[i].connected = tape.player_participates[i];
3783   }
3784   else if (network.enabled)
3785   {
3786     // add team mode players connected over the network (needed for correct
3787     // assignment of player figures from level to locally playing players)
3788
3789     for (i = 0; i < MAX_PLAYERS; i++)
3790       if (stored_player[i].connected_network)
3791         stored_player[i].connected = TRUE;
3792   }
3793   else if (game.team_mode)
3794   {
3795     // try to guess locally connected team mode players (needed for correct
3796     // assignment of player figures from level to locally playing players)
3797
3798     for (i = 0; i < MAX_PLAYERS; i++)
3799       if (setup.input[i].use_joystick ||
3800           setup.input[i].key.left != KSYM_UNDEFINED)
3801         stored_player[i].connected = TRUE;
3802   }
3803
3804 #if DEBUG_INIT_PLAYER
3805   DebugPrintPlayerStatus("Player status after level initialization");
3806 #endif
3807
3808 #if DEBUG_INIT_PLAYER
3809   if (options.debug)
3810     printf("Reassigning players ...\n");
3811 #endif
3812
3813   // check if any connected player was not found in playfield
3814   for (i = 0; i < MAX_PLAYERS; i++)
3815   {
3816     struct PlayerInfo *player = &stored_player[i];
3817
3818     if (player->connected && !player->present)
3819     {
3820       struct PlayerInfo *field_player = NULL;
3821
3822 #if DEBUG_INIT_PLAYER
3823       if (options.debug)
3824         printf("- looking for field player for player %d ...\n", i + 1);
3825 #endif
3826
3827       // assign first free player found that is present in the playfield
3828
3829       // first try: look for unmapped playfield player that is not connected
3830       for (j = 0; j < MAX_PLAYERS; j++)
3831         if (field_player == NULL &&
3832             stored_player[j].present &&
3833             !stored_player[j].mapped &&
3834             !stored_player[j].connected)
3835           field_player = &stored_player[j];
3836
3837       // second try: look for *any* unmapped playfield player
3838       for (j = 0; j < MAX_PLAYERS; j++)
3839         if (field_player == NULL &&
3840             stored_player[j].present &&
3841             !stored_player[j].mapped)
3842           field_player = &stored_player[j];
3843
3844       if (field_player != NULL)
3845       {
3846         int jx = field_player->jx, jy = field_player->jy;
3847
3848 #if DEBUG_INIT_PLAYER
3849         if (options.debug)
3850           printf("- found player %d\n", field_player->index_nr + 1);
3851 #endif
3852
3853         player->present = FALSE;
3854         player->active = FALSE;
3855
3856         field_player->present = TRUE;
3857         field_player->active = TRUE;
3858
3859         /*
3860         player->initial_element = field_player->initial_element;
3861         player->artwork_element = field_player->artwork_element;
3862
3863         player->block_last_field       = field_player->block_last_field;
3864         player->block_delay_adjustment = field_player->block_delay_adjustment;
3865         */
3866
3867         StorePlayer[jx][jy] = field_player->element_nr;
3868
3869         field_player->jx = field_player->last_jx = jx;
3870         field_player->jy = field_player->last_jy = jy;
3871
3872         if (local_player == player)
3873           local_player = field_player;
3874
3875         map_player_action[field_player->index_nr] = i;
3876
3877         field_player->mapped = TRUE;
3878
3879 #if DEBUG_INIT_PLAYER
3880         if (options.debug)
3881           printf("- map_player_action[%d] == %d\n",
3882                  field_player->index_nr + 1, i + 1);
3883 #endif
3884       }
3885     }
3886
3887     if (player->connected && player->present)
3888       player->mapped = TRUE;
3889   }
3890
3891 #if DEBUG_INIT_PLAYER
3892   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3893 #endif
3894
3895 #else
3896
3897   // check if any connected player was not found in playfield
3898   for (i = 0; i < MAX_PLAYERS; i++)
3899   {
3900     struct PlayerInfo *player = &stored_player[i];
3901
3902     if (player->connected && !player->present)
3903     {
3904       for (j = 0; j < MAX_PLAYERS; j++)
3905       {
3906         struct PlayerInfo *field_player = &stored_player[j];
3907         int jx = field_player->jx, jy = field_player->jy;
3908
3909         // assign first free player found that is present in the playfield
3910         if (field_player->present && !field_player->connected)
3911         {
3912           player->present = TRUE;
3913           player->active = TRUE;
3914
3915           field_player->present = FALSE;
3916           field_player->active = FALSE;
3917
3918           player->initial_element = field_player->initial_element;
3919           player->artwork_element = field_player->artwork_element;
3920
3921           player->block_last_field       = field_player->block_last_field;
3922           player->block_delay_adjustment = field_player->block_delay_adjustment;
3923
3924           StorePlayer[jx][jy] = player->element_nr;
3925
3926           player->jx = player->last_jx = jx;
3927           player->jy = player->last_jy = jy;
3928
3929           break;
3930         }
3931       }
3932     }
3933   }
3934 #endif
3935
3936 #if 0
3937   printf("::: local_player->present == %d\n", local_player->present);
3938 #endif
3939
3940   // set focus to local player for network games, else to all players
3941   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3942   game.centered_player_nr_next = game.centered_player_nr;
3943   game.set_centered_player = FALSE;
3944   game.set_centered_player_wrap = FALSE;
3945
3946   if (network_playing && tape.recording)
3947   {
3948     // store client dependent player focus when recording network games
3949     tape.centered_player_nr_next = game.centered_player_nr_next;
3950     tape.set_centered_player = TRUE;
3951   }
3952
3953   if (tape.playing)
3954   {
3955     // when playing a tape, eliminate all players who do not participate
3956
3957 #if USE_NEW_PLAYER_ASSIGNMENTS
3958
3959     if (!game.team_mode)
3960     {
3961       for (i = 0; i < MAX_PLAYERS; i++)
3962       {
3963         if (stored_player[i].active &&
3964             !tape.player_participates[map_player_action[i]])
3965         {
3966           struct PlayerInfo *player = &stored_player[i];
3967           int jx = player->jx, jy = player->jy;
3968
3969 #if DEBUG_INIT_PLAYER
3970           if (options.debug)
3971             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3972 #endif
3973
3974           player->active = FALSE;
3975           StorePlayer[jx][jy] = 0;
3976           Feld[jx][jy] = EL_EMPTY;
3977         }
3978       }
3979     }
3980
3981 #else
3982
3983     for (i = 0; i < MAX_PLAYERS; i++)
3984     {
3985       if (stored_player[i].active &&
3986           !tape.player_participates[i])
3987       {
3988         struct PlayerInfo *player = &stored_player[i];
3989         int jx = player->jx, jy = player->jy;
3990
3991         player->active = FALSE;
3992         StorePlayer[jx][jy] = 0;
3993         Feld[jx][jy] = EL_EMPTY;
3994       }
3995     }
3996 #endif
3997   }
3998   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3999   {
4000     // when in single player mode, eliminate all but the local player
4001
4002     for (i = 0; i < MAX_PLAYERS; i++)
4003     {
4004       struct PlayerInfo *player = &stored_player[i];
4005
4006       if (player->active && player != local_player)
4007       {
4008         int jx = player->jx, jy = player->jy;
4009
4010         player->active = FALSE;
4011         player->present = FALSE;
4012
4013         StorePlayer[jx][jy] = 0;
4014         Feld[jx][jy] = EL_EMPTY;
4015       }
4016     }
4017   }
4018
4019   for (i = 0; i < MAX_PLAYERS; i++)
4020     if (stored_player[i].active)
4021       game.players_still_needed++;
4022
4023   if (level.solved_by_one_player)
4024     game.players_still_needed = 1;
4025
4026   // when recording the game, store which players take part in the game
4027   if (tape.recording)
4028   {
4029 #if USE_NEW_PLAYER_ASSIGNMENTS
4030     for (i = 0; i < MAX_PLAYERS; i++)
4031       if (stored_player[i].connected)
4032         tape.player_participates[i] = TRUE;
4033 #else
4034     for (i = 0; i < MAX_PLAYERS; i++)
4035       if (stored_player[i].active)
4036         tape.player_participates[i] = TRUE;
4037 #endif
4038   }
4039
4040 #if DEBUG_INIT_PLAYER
4041   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4042 #endif
4043
4044   if (BorderElement == EL_EMPTY)
4045   {
4046     SBX_Left = 0;
4047     SBX_Right = lev_fieldx - SCR_FIELDX;
4048     SBY_Upper = 0;
4049     SBY_Lower = lev_fieldy - SCR_FIELDY;
4050   }
4051   else
4052   {
4053     SBX_Left = -1;
4054     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4055     SBY_Upper = -1;
4056     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4057   }
4058
4059   if (full_lev_fieldx <= SCR_FIELDX)
4060     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4061   if (full_lev_fieldy <= SCR_FIELDY)
4062     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4063
4064   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4065     SBX_Left--;
4066   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4067     SBY_Upper--;
4068
4069   // if local player not found, look for custom element that might create
4070   // the player (make some assumptions about the right custom element)
4071   if (!local_player->present)
4072   {
4073     int start_x = 0, start_y = 0;
4074     int found_rating = 0;
4075     int found_element = EL_UNDEFINED;
4076     int player_nr = local_player->index_nr;
4077
4078     SCAN_PLAYFIELD(x, y)
4079     {
4080       int element = Feld[x][y];
4081       int content;
4082       int xx, yy;
4083       boolean is_player;
4084
4085       if (level.use_start_element[player_nr] &&
4086           level.start_element[player_nr] == element &&
4087           found_rating < 4)
4088       {
4089         start_x = x;
4090         start_y = y;
4091
4092         found_rating = 4;
4093         found_element = element;
4094       }
4095
4096       if (!IS_CUSTOM_ELEMENT(element))
4097         continue;
4098
4099       if (CAN_CHANGE(element))
4100       {
4101         for (i = 0; i < element_info[element].num_change_pages; i++)
4102         {
4103           // check for player created from custom element as single target
4104           content = element_info[element].change_page[i].target_element;
4105           is_player = ELEM_IS_PLAYER(content);
4106
4107           if (is_player && (found_rating < 3 ||
4108                             (found_rating == 3 && element < found_element)))
4109           {
4110             start_x = x;
4111             start_y = y;
4112
4113             found_rating = 3;
4114             found_element = element;
4115           }
4116         }
4117       }
4118
4119       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4120       {
4121         // check for player created from custom element as explosion content
4122         content = element_info[element].content.e[xx][yy];
4123         is_player = ELEM_IS_PLAYER(content);
4124
4125         if (is_player && (found_rating < 2 ||
4126                           (found_rating == 2 && element < found_element)))
4127         {
4128           start_x = x + xx - 1;
4129           start_y = y + yy - 1;
4130
4131           found_rating = 2;
4132           found_element = element;
4133         }
4134
4135         if (!CAN_CHANGE(element))
4136           continue;
4137
4138         for (i = 0; i < element_info[element].num_change_pages; i++)
4139         {
4140           // check for player created from custom element as extended target
4141           content =
4142             element_info[element].change_page[i].target_content.e[xx][yy];
4143
4144           is_player = ELEM_IS_PLAYER(content);
4145
4146           if (is_player && (found_rating < 1 ||
4147                             (found_rating == 1 && element < found_element)))
4148           {
4149             start_x = x + xx - 1;
4150             start_y = y + yy - 1;
4151
4152             found_rating = 1;
4153             found_element = element;
4154           }
4155         }
4156       }
4157     }
4158
4159     scroll_x = SCROLL_POSITION_X(start_x);
4160     scroll_y = SCROLL_POSITION_Y(start_y);
4161   }
4162   else
4163   {
4164     scroll_x = SCROLL_POSITION_X(local_player->jx);
4165     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4166   }
4167
4168   // !!! FIX THIS (START) !!!
4169   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4170   {
4171     InitGameEngine_EM();
4172   }
4173   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4174   {
4175     InitGameEngine_SP();
4176   }
4177   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4178   {
4179     InitGameEngine_MM();
4180   }
4181   else
4182   {
4183     DrawLevel(REDRAW_FIELD);
4184     DrawAllPlayers();
4185
4186     // after drawing the level, correct some elements
4187     if (game.timegate_time_left == 0)
4188       CloseAllOpenTimegates();
4189   }
4190
4191   // blit playfield from scroll buffer to normal back buffer for fading in
4192   BlitScreenToBitmap(backbuffer);
4193   // !!! FIX THIS (END) !!!
4194
4195   DrawMaskedBorder(fade_mask);
4196
4197   FadeIn(fade_mask);
4198
4199 #if 1
4200   // full screen redraw is required at this point in the following cases:
4201   // - special editor door undrawn when game was started from level editor
4202   // - drawing area (playfield) was changed and has to be removed completely
4203   redraw_mask = REDRAW_ALL;
4204   BackToFront();
4205 #endif
4206
4207   if (!game.restart_level)
4208   {
4209     // copy default game door content to main double buffer
4210
4211     // !!! CHECK AGAIN !!!
4212     SetPanelBackground();
4213     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4214     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4215   }
4216
4217   SetPanelBackground();
4218   SetDrawBackgroundMask(REDRAW_DOOR_1);
4219
4220   UpdateAndDisplayGameControlValues();
4221
4222   if (!game.restart_level)
4223   {
4224     UnmapGameButtons();
4225     UnmapTapeButtons();
4226
4227     FreeGameButtons();
4228     CreateGameButtons();
4229
4230     MapGameButtons();
4231     MapTapeButtons();
4232
4233     // copy actual game door content to door double buffer for OpenDoor()
4234     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4235
4236     OpenDoor(DOOR_OPEN_ALL);
4237
4238     KeyboardAutoRepeatOffUnlessAutoplay();
4239
4240 #if DEBUG_INIT_PLAYER
4241     DebugPrintPlayerStatus("Player status (final)");
4242 #endif
4243   }
4244
4245   UnmapAllGadgets();
4246
4247   MapGameButtons();
4248   MapTapeButtons();
4249
4250   if (!game.restart_level && !tape.playing)
4251   {
4252     LevelStats_incPlayed(level_nr);
4253
4254     SaveLevelSetup_SeriesInfo();
4255   }
4256
4257   game.restart_level = FALSE;
4258   game.restart_game_message = NULL;
4259   game.request_active = FALSE;
4260
4261   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4262     InitGameActions_MM();
4263
4264   SaveEngineSnapshotToListInitial();
4265
4266   if (!game.restart_level)
4267   {
4268     PlaySound(SND_GAME_STARTING);
4269
4270     if (setup.sound_music)
4271       PlayLevelMusic();
4272   }
4273 }
4274
4275 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4276                         int actual_player_x, int actual_player_y)
4277 {
4278   // this is used for non-R'n'D game engines to update certain engine values
4279
4280   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4281   {
4282     actual_player_x = correctLevelPosX_EM(actual_player_x);
4283     actual_player_y = correctLevelPosY_EM(actual_player_y);
4284   }
4285
4286   // needed to determine if sounds are played within the visible screen area
4287   scroll_x = actual_scroll_x;
4288   scroll_y = actual_scroll_y;
4289
4290   // needed to get player position for "follow finger" playing input method
4291   local_player->jx = actual_player_x;
4292   local_player->jy = actual_player_y;
4293 }
4294
4295 void InitMovDir(int x, int y)
4296 {
4297   int i, element = Feld[x][y];
4298   static int xy[4][2] =
4299   {
4300     {  0, +1 },
4301     { +1,  0 },
4302     {  0, -1 },
4303     { -1,  0 }
4304   };
4305   static int direction[3][4] =
4306   {
4307     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4308     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4309     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4310   };
4311
4312   switch (element)
4313   {
4314     case EL_BUG_RIGHT:
4315     case EL_BUG_UP:
4316     case EL_BUG_LEFT:
4317     case EL_BUG_DOWN:
4318       Feld[x][y] = EL_BUG;
4319       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4320       break;
4321
4322     case EL_SPACESHIP_RIGHT:
4323     case EL_SPACESHIP_UP:
4324     case EL_SPACESHIP_LEFT:
4325     case EL_SPACESHIP_DOWN:
4326       Feld[x][y] = EL_SPACESHIP;
4327       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4328       break;
4329
4330     case EL_BD_BUTTERFLY_RIGHT:
4331     case EL_BD_BUTTERFLY_UP:
4332     case EL_BD_BUTTERFLY_LEFT:
4333     case EL_BD_BUTTERFLY_DOWN:
4334       Feld[x][y] = EL_BD_BUTTERFLY;
4335       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4336       break;
4337
4338     case EL_BD_FIREFLY_RIGHT:
4339     case EL_BD_FIREFLY_UP:
4340     case EL_BD_FIREFLY_LEFT:
4341     case EL_BD_FIREFLY_DOWN:
4342       Feld[x][y] = EL_BD_FIREFLY;
4343       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4344       break;
4345
4346     case EL_PACMAN_RIGHT:
4347     case EL_PACMAN_UP:
4348     case EL_PACMAN_LEFT:
4349     case EL_PACMAN_DOWN:
4350       Feld[x][y] = EL_PACMAN;
4351       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4352       break;
4353
4354     case EL_YAMYAM_LEFT:
4355     case EL_YAMYAM_RIGHT:
4356     case EL_YAMYAM_UP:
4357     case EL_YAMYAM_DOWN:
4358       Feld[x][y] = EL_YAMYAM;
4359       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4360       break;
4361
4362     case EL_SP_SNIKSNAK:
4363       MovDir[x][y] = MV_UP;
4364       break;
4365
4366     case EL_SP_ELECTRON:
4367       MovDir[x][y] = MV_LEFT;
4368       break;
4369
4370     case EL_MOLE_LEFT:
4371     case EL_MOLE_RIGHT:
4372     case EL_MOLE_UP:
4373     case EL_MOLE_DOWN:
4374       Feld[x][y] = EL_MOLE;
4375       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4376       break;
4377
4378     default:
4379       if (IS_CUSTOM_ELEMENT(element))
4380       {
4381         struct ElementInfo *ei = &element_info[element];
4382         int move_direction_initial = ei->move_direction_initial;
4383         int move_pattern = ei->move_pattern;
4384
4385         if (move_direction_initial == MV_START_PREVIOUS)
4386         {
4387           if (MovDir[x][y] != MV_NONE)
4388             return;
4389
4390           move_direction_initial = MV_START_AUTOMATIC;
4391         }
4392
4393         if (move_direction_initial == MV_START_RANDOM)
4394           MovDir[x][y] = 1 << RND(4);
4395         else if (move_direction_initial & MV_ANY_DIRECTION)
4396           MovDir[x][y] = move_direction_initial;
4397         else if (move_pattern == MV_ALL_DIRECTIONS ||
4398                  move_pattern == MV_TURNING_LEFT ||
4399                  move_pattern == MV_TURNING_RIGHT ||
4400                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4401                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4402                  move_pattern == MV_TURNING_RANDOM)
4403           MovDir[x][y] = 1 << RND(4);
4404         else if (move_pattern == MV_HORIZONTAL)
4405           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4406         else if (move_pattern == MV_VERTICAL)
4407           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4408         else if (move_pattern & MV_ANY_DIRECTION)
4409           MovDir[x][y] = element_info[element].move_pattern;
4410         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4411                  move_pattern == MV_ALONG_RIGHT_SIDE)
4412         {
4413           // use random direction as default start direction
4414           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4415             MovDir[x][y] = 1 << RND(4);
4416
4417           for (i = 0; i < NUM_DIRECTIONS; i++)
4418           {
4419             int x1 = x + xy[i][0];
4420             int y1 = y + xy[i][1];
4421
4422             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4423             {
4424               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4425                 MovDir[x][y] = direction[0][i];
4426               else
4427                 MovDir[x][y] = direction[1][i];
4428
4429               break;
4430             }
4431           }
4432         }                
4433       }
4434       else
4435       {
4436         MovDir[x][y] = 1 << RND(4);
4437
4438         if (element != EL_BUG &&
4439             element != EL_SPACESHIP &&
4440             element != EL_BD_BUTTERFLY &&
4441             element != EL_BD_FIREFLY)
4442           break;
4443
4444         for (i = 0; i < NUM_DIRECTIONS; i++)
4445         {
4446           int x1 = x + xy[i][0];
4447           int y1 = y + xy[i][1];
4448
4449           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4450           {
4451             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4452             {
4453               MovDir[x][y] = direction[0][i];
4454               break;
4455             }
4456             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4457                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4458             {
4459               MovDir[x][y] = direction[1][i];
4460               break;
4461             }
4462           }
4463         }
4464       }
4465       break;
4466   }
4467
4468   GfxDir[x][y] = MovDir[x][y];
4469 }
4470
4471 void InitAmoebaNr(int x, int y)
4472 {
4473   int i;
4474   int group_nr = AmoebeNachbarNr(x, y);
4475
4476   if (group_nr == 0)
4477   {
4478     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4479     {
4480       if (AmoebaCnt[i] == 0)
4481       {
4482         group_nr = i;
4483         break;
4484       }
4485     }
4486   }
4487
4488   AmoebaNr[x][y] = group_nr;
4489   AmoebaCnt[group_nr]++;
4490   AmoebaCnt2[group_nr]++;
4491 }
4492
4493 static void LevelSolved(void)
4494 {
4495   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4496       game.players_still_needed > 0)
4497     return;
4498
4499   game.LevelSolved = TRUE;
4500   game.GameOver = TRUE;
4501
4502   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4503                       game_em.lev->score :
4504                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4505                       game_mm.score :
4506                       game.score);
4507   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4508                        MM_HEALTH(game_mm.laser_overload_value) :
4509                        game.health);
4510
4511   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4512   game.LevelSolved_CountingScore = game.score_final;
4513   game.LevelSolved_CountingHealth = game.health_final;
4514 }
4515
4516 void GameWon(void)
4517 {
4518   static int time_count_steps;
4519   static int time, time_final;
4520   static int score, score_final;
4521   static int health, health_final;
4522   static int game_over_delay_1 = 0;
4523   static int game_over_delay_2 = 0;
4524   static int game_over_delay_3 = 0;
4525   int game_over_delay_value_1 = 50;
4526   int game_over_delay_value_2 = 25;
4527   int game_over_delay_value_3 = 50;
4528
4529   if (!game.LevelSolved_GameWon)
4530   {
4531     int i;
4532
4533     // do not start end game actions before the player stops moving (to exit)
4534     if (local_player->active && local_player->MovPos)
4535       return;
4536
4537     game.LevelSolved_GameWon = TRUE;
4538     game.LevelSolved_SaveTape = tape.recording;
4539     game.LevelSolved_SaveScore = !tape.playing;
4540
4541     if (!tape.playing)
4542     {
4543       LevelStats_incSolved(level_nr);
4544
4545       SaveLevelSetup_SeriesInfo();
4546     }
4547
4548     if (tape.auto_play)         // tape might already be stopped here
4549       tape.auto_play_level_solved = TRUE;
4550
4551     TapeStop();
4552
4553     game_over_delay_1 = 0;
4554     game_over_delay_2 = 0;
4555     game_over_delay_3 = game_over_delay_value_3;
4556
4557     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4558     score = score_final = game.score_final;
4559     health = health_final = game.health_final;
4560
4561     if (level.score[SC_TIME_BONUS] > 0)
4562     {
4563       if (TimeLeft > 0)
4564       {
4565         time_final = 0;
4566         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4567       }
4568       else if (game.no_time_limit && TimePlayed < 999)
4569       {
4570         time_final = 999;
4571         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4572       }
4573
4574       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4575
4576       game_over_delay_1 = game_over_delay_value_1;
4577
4578       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4579       {
4580         health_final = 0;
4581         score_final += health * level.score[SC_TIME_BONUS];
4582
4583         game_over_delay_2 = game_over_delay_value_2;
4584       }
4585
4586       game.score_final = score_final;
4587       game.health_final = health_final;
4588     }
4589
4590     if (level_editor_test_game)
4591     {
4592       time = time_final;
4593       score = score_final;
4594
4595       game.LevelSolved_CountingTime = time;
4596       game.LevelSolved_CountingScore = score;
4597
4598       game_panel_controls[GAME_PANEL_TIME].value = time;
4599       game_panel_controls[GAME_PANEL_SCORE].value = score;
4600
4601       DisplayGameControlValues();
4602     }
4603
4604     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4605     {
4606       // check if last player has left the level
4607       if (game.exit_x >= 0 &&
4608           game.exit_y >= 0)
4609       {
4610         int x = game.exit_x;
4611         int y = game.exit_y;
4612         int element = Feld[x][y];
4613
4614         // close exit door after last player
4615         if ((game.all_players_gone &&
4616              (element == EL_EXIT_OPEN ||
4617               element == EL_SP_EXIT_OPEN ||
4618               element == EL_STEEL_EXIT_OPEN)) ||
4619             element == EL_EM_EXIT_OPEN ||
4620             element == EL_EM_STEEL_EXIT_OPEN)
4621         {
4622
4623           Feld[x][y] =
4624             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4625              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4626              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4627              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4628              EL_EM_STEEL_EXIT_CLOSING);
4629
4630           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4631         }
4632
4633         // player disappears
4634         DrawLevelField(x, y);
4635       }
4636
4637       for (i = 0; i < MAX_PLAYERS; i++)
4638       {
4639         struct PlayerInfo *player = &stored_player[i];
4640
4641         if (player->present)
4642         {
4643           RemovePlayer(player);
4644
4645           // player disappears
4646           DrawLevelField(player->jx, player->jy);
4647         }
4648       }
4649     }
4650
4651     PlaySound(SND_GAME_WINNING);
4652   }
4653
4654   if (game_over_delay_1 > 0)
4655   {
4656     game_over_delay_1--;
4657
4658     return;
4659   }
4660
4661   if (time != time_final)
4662   {
4663     int time_to_go = ABS(time_final - time);
4664     int time_count_dir = (time < time_final ? +1 : -1);
4665
4666     if (time_to_go < time_count_steps)
4667       time_count_steps = 1;
4668
4669     time  += time_count_steps * time_count_dir;
4670     score += time_count_steps * level.score[SC_TIME_BONUS];
4671
4672     game.LevelSolved_CountingTime = time;
4673     game.LevelSolved_CountingScore = score;
4674
4675     game_panel_controls[GAME_PANEL_TIME].value = time;
4676     game_panel_controls[GAME_PANEL_SCORE].value = score;
4677
4678     DisplayGameControlValues();
4679
4680     if (time == time_final)
4681       StopSound(SND_GAME_LEVELTIME_BONUS);
4682     else if (setup.sound_loops)
4683       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4684     else
4685       PlaySound(SND_GAME_LEVELTIME_BONUS);
4686
4687     return;
4688   }
4689
4690   if (game_over_delay_2 > 0)
4691   {
4692     game_over_delay_2--;
4693
4694     return;
4695   }
4696
4697   if (health != health_final)
4698   {
4699     int health_count_dir = (health < health_final ? +1 : -1);
4700
4701     health += health_count_dir;
4702     score  += level.score[SC_TIME_BONUS];
4703
4704     game.LevelSolved_CountingHealth = health;
4705     game.LevelSolved_CountingScore = score;
4706
4707     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4708     game_panel_controls[GAME_PANEL_SCORE].value = score;
4709
4710     DisplayGameControlValues();
4711
4712     if (health == health_final)
4713       StopSound(SND_GAME_LEVELTIME_BONUS);
4714     else if (setup.sound_loops)
4715       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4716     else
4717       PlaySound(SND_GAME_LEVELTIME_BONUS);
4718
4719     return;
4720   }
4721
4722   game.panel.active = FALSE;
4723
4724   if (game_over_delay_3 > 0)
4725   {
4726     game_over_delay_3--;
4727
4728     return;
4729   }
4730
4731   GameEnd();
4732 }
4733
4734 void GameEnd(void)
4735 {
4736   // used instead of "level_nr" (needed for network games)
4737   int last_level_nr = levelset.level_nr;
4738   int hi_pos;
4739
4740   game.LevelSolved_GameEnd = TRUE;
4741
4742   if (game.LevelSolved_SaveTape)
4743   {
4744     // make sure that request dialog to save tape does not open door again
4745     if (!global.use_envelope_request)
4746       CloseDoor(DOOR_CLOSE_1);
4747
4748     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4749   }
4750
4751   // if no tape is to be saved, close both doors simultaneously
4752   CloseDoor(DOOR_CLOSE_ALL);
4753
4754   if (level_editor_test_game)
4755   {
4756     SetGameStatus(GAME_MODE_MAIN);
4757
4758     DrawMainMenu();
4759
4760     return;
4761   }
4762
4763   if (!game.LevelSolved_SaveScore)
4764   {
4765     SetGameStatus(GAME_MODE_MAIN);
4766
4767     DrawMainMenu();
4768
4769     return;
4770   }
4771
4772   if (level_nr == leveldir_current->handicap_level)
4773   {
4774     leveldir_current->handicap_level++;
4775
4776     SaveLevelSetup_SeriesInfo();
4777   }
4778
4779   if (setup.increment_levels &&
4780       level_nr < leveldir_current->last_level &&
4781       !network_playing)
4782   {
4783     level_nr++;         // advance to next level
4784     TapeErase();        // start with empty tape
4785
4786     if (setup.auto_play_next_level)
4787     {
4788       LoadLevel(level_nr);
4789
4790       SaveLevelSetup_SeriesInfo();
4791     }
4792   }
4793
4794   hi_pos = NewHiScore(last_level_nr);
4795
4796   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4797   {
4798     SetGameStatus(GAME_MODE_SCORES);
4799
4800     DrawHallOfFame(last_level_nr, hi_pos);
4801   }
4802   else if (setup.auto_play_next_level && setup.increment_levels &&
4803            last_level_nr < leveldir_current->last_level &&
4804            !network_playing)
4805   {
4806     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4807   }
4808   else
4809   {
4810     SetGameStatus(GAME_MODE_MAIN);
4811
4812     DrawMainMenu();
4813   }
4814 }
4815
4816 int NewHiScore(int level_nr)
4817 {
4818   int k, l;
4819   int position = -1;
4820   boolean one_score_entry_per_name = !program.many_scores_per_name;
4821
4822   LoadScore(level_nr);
4823
4824   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4825       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4826     return -1;
4827
4828   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4829   {
4830     if (game.score_final > highscore[k].Score)
4831     {
4832       // player has made it to the hall of fame
4833
4834       if (k < MAX_SCORE_ENTRIES - 1)
4835       {
4836         int m = MAX_SCORE_ENTRIES - 1;
4837
4838         if (one_score_entry_per_name)
4839         {
4840           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4841             if (strEqual(setup.player_name, highscore[l].Name))
4842               m = l;
4843
4844           if (m == k)   // player's new highscore overwrites his old one
4845             goto put_into_list;
4846         }
4847
4848         for (l = m; l > k; l--)
4849         {
4850           strcpy(highscore[l].Name, highscore[l - 1].Name);
4851           highscore[l].Score = highscore[l - 1].Score;
4852         }
4853       }
4854
4855       put_into_list:
4856
4857       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4858       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4859       highscore[k].Score = game.score_final;
4860       position = k;
4861
4862       break;
4863     }
4864     else if (one_score_entry_per_name &&
4865              !strncmp(setup.player_name, highscore[k].Name,
4866                       MAX_PLAYER_NAME_LEN))
4867       break;    // player already there with a higher score
4868   }
4869
4870   if (position >= 0) 
4871     SaveScore(level_nr);
4872
4873   return position;
4874 }
4875
4876 static int getElementMoveStepsizeExt(int x, int y, int direction)
4877 {
4878   int element = Feld[x][y];
4879   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4880   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4881   int horiz_move = (dx != 0);
4882   int sign = (horiz_move ? dx : dy);
4883   int step = sign * element_info[element].move_stepsize;
4884
4885   // special values for move stepsize for spring and things on conveyor belt
4886   if (horiz_move)
4887   {
4888     if (CAN_FALL(element) &&
4889         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4890       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4891     else if (element == EL_SPRING)
4892       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4893   }
4894
4895   return step;
4896 }
4897
4898 static int getElementMoveStepsize(int x, int y)
4899 {
4900   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4901 }
4902
4903 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4904 {
4905   if (player->GfxAction != action || player->GfxDir != dir)
4906   {
4907     player->GfxAction = action;
4908     player->GfxDir = dir;
4909     player->Frame = 0;
4910     player->StepFrame = 0;
4911   }
4912 }
4913
4914 static void ResetGfxFrame(int x, int y)
4915 {
4916   // profiling showed that "autotest" spends 10~20% of its time in this function
4917   if (DrawingDeactivatedField())
4918     return;
4919
4920   int element = Feld[x][y];
4921   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4922
4923   if (graphic_info[graphic].anim_global_sync)
4924     GfxFrame[x][y] = FrameCounter;
4925   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4926     GfxFrame[x][y] = CustomValue[x][y];
4927   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4928     GfxFrame[x][y] = element_info[element].collect_score;
4929   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4930     GfxFrame[x][y] = ChangeDelay[x][y];
4931 }
4932
4933 static void ResetGfxAnimation(int x, int y)
4934 {
4935   GfxAction[x][y] = ACTION_DEFAULT;
4936   GfxDir[x][y] = MovDir[x][y];
4937   GfxFrame[x][y] = 0;
4938
4939   ResetGfxFrame(x, y);
4940 }
4941
4942 static void ResetRandomAnimationValue(int x, int y)
4943 {
4944   GfxRandom[x][y] = INIT_GFX_RANDOM();
4945 }
4946
4947 static void InitMovingField(int x, int y, int direction)
4948 {
4949   int element = Feld[x][y];
4950   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4951   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4952   int newx = x + dx;
4953   int newy = y + dy;
4954   boolean is_moving_before, is_moving_after;
4955
4956   // check if element was/is moving or being moved before/after mode change
4957   is_moving_before = (WasJustMoving[x][y] != 0);
4958   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4959
4960   // reset animation only for moving elements which change direction of moving
4961   // or which just started or stopped moving
4962   // (else CEs with property "can move" / "not moving" are reset each frame)
4963   if (is_moving_before != is_moving_after ||
4964       direction != MovDir[x][y])
4965     ResetGfxAnimation(x, y);
4966
4967   MovDir[x][y] = direction;
4968   GfxDir[x][y] = direction;
4969
4970   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4971                      direction == MV_DOWN && CAN_FALL(element) ?
4972                      ACTION_FALLING : ACTION_MOVING);
4973
4974   // this is needed for CEs with property "can move" / "not moving"
4975
4976   if (is_moving_after)
4977   {
4978     if (Feld[newx][newy] == EL_EMPTY)
4979       Feld[newx][newy] = EL_BLOCKED;
4980
4981     MovDir[newx][newy] = MovDir[x][y];
4982
4983     CustomValue[newx][newy] = CustomValue[x][y];
4984
4985     GfxFrame[newx][newy] = GfxFrame[x][y];
4986     GfxRandom[newx][newy] = GfxRandom[x][y];
4987     GfxAction[newx][newy] = GfxAction[x][y];
4988     GfxDir[newx][newy] = GfxDir[x][y];
4989   }
4990 }
4991
4992 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4993 {
4994   int direction = MovDir[x][y];
4995   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4996   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4997
4998   *goes_to_x = newx;
4999   *goes_to_y = newy;
5000 }
5001
5002 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5003 {
5004   int oldx = x, oldy = y;
5005   int direction = MovDir[x][y];
5006
5007   if (direction == MV_LEFT)
5008     oldx++;
5009   else if (direction == MV_RIGHT)
5010     oldx--;
5011   else if (direction == MV_UP)
5012     oldy++;
5013   else if (direction == MV_DOWN)
5014     oldy--;
5015
5016   *comes_from_x = oldx;
5017   *comes_from_y = oldy;
5018 }
5019
5020 static int MovingOrBlocked2Element(int x, int y)
5021 {
5022   int element = Feld[x][y];
5023
5024   if (element == EL_BLOCKED)
5025   {
5026     int oldx, oldy;
5027
5028     Blocked2Moving(x, y, &oldx, &oldy);
5029     return Feld[oldx][oldy];
5030   }
5031   else
5032     return element;
5033 }
5034
5035 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5036 {
5037   // like MovingOrBlocked2Element(), but if element is moving
5038   // and (x,y) is the field the moving element is just leaving,
5039   // return EL_BLOCKED instead of the element value
5040   int element = Feld[x][y];
5041
5042   if (IS_MOVING(x, y))
5043   {
5044     if (element == EL_BLOCKED)
5045     {
5046       int oldx, oldy;
5047
5048       Blocked2Moving(x, y, &oldx, &oldy);
5049       return Feld[oldx][oldy];
5050     }
5051     else
5052       return EL_BLOCKED;
5053   }
5054   else
5055     return element;
5056 }
5057
5058 static void RemoveField(int x, int y)
5059 {
5060   Feld[x][y] = EL_EMPTY;
5061
5062   MovPos[x][y] = 0;
5063   MovDir[x][y] = 0;
5064   MovDelay[x][y] = 0;
5065
5066   CustomValue[x][y] = 0;
5067
5068   AmoebaNr[x][y] = 0;
5069   ChangeDelay[x][y] = 0;
5070   ChangePage[x][y] = -1;
5071   Pushed[x][y] = FALSE;
5072
5073   GfxElement[x][y] = EL_UNDEFINED;
5074   GfxAction[x][y] = ACTION_DEFAULT;
5075   GfxDir[x][y] = MV_NONE;
5076 }
5077
5078 static void RemoveMovingField(int x, int y)
5079 {
5080   int oldx = x, oldy = y, newx = x, newy = y;
5081   int element = Feld[x][y];
5082   int next_element = EL_UNDEFINED;
5083
5084   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5085     return;
5086
5087   if (IS_MOVING(x, y))
5088   {
5089     Moving2Blocked(x, y, &newx, &newy);
5090
5091     if (Feld[newx][newy] != EL_BLOCKED)
5092     {
5093       // element is moving, but target field is not free (blocked), but
5094       // already occupied by something different (example: acid pool);
5095       // in this case, only remove the moving field, but not the target
5096
5097       RemoveField(oldx, oldy);
5098
5099       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5100
5101       TEST_DrawLevelField(oldx, oldy);
5102
5103       return;
5104     }
5105   }
5106   else if (element == EL_BLOCKED)
5107   {
5108     Blocked2Moving(x, y, &oldx, &oldy);
5109     if (!IS_MOVING(oldx, oldy))
5110       return;
5111   }
5112
5113   if (element == EL_BLOCKED &&
5114       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5115        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5116        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5117        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5118        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5119        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5120     next_element = get_next_element(Feld[oldx][oldy]);
5121
5122   RemoveField(oldx, oldy);
5123   RemoveField(newx, newy);
5124
5125   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5126
5127   if (next_element != EL_UNDEFINED)
5128     Feld[oldx][oldy] = next_element;
5129
5130   TEST_DrawLevelField(oldx, oldy);
5131   TEST_DrawLevelField(newx, newy);
5132 }
5133
5134 void DrawDynamite(int x, int y)
5135 {
5136   int sx = SCREENX(x), sy = SCREENY(y);
5137   int graphic = el2img(Feld[x][y]);
5138   int frame;
5139
5140   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5141     return;
5142
5143   if (IS_WALKABLE_INSIDE(Back[x][y]))
5144     return;
5145
5146   if (Back[x][y])
5147     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5148   else if (Store[x][y])
5149     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5150
5151   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5152
5153   if (Back[x][y] || Store[x][y])
5154     DrawGraphicThruMask(sx, sy, graphic, frame);
5155   else
5156     DrawGraphic(sx, sy, graphic, frame);
5157 }
5158
5159 static void CheckDynamite(int x, int y)
5160 {
5161   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5162   {
5163     MovDelay[x][y]--;
5164
5165     if (MovDelay[x][y] != 0)
5166     {
5167       DrawDynamite(x, y);
5168       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5169
5170       return;
5171     }
5172   }
5173
5174   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5175
5176   Bang(x, y);
5177 }
5178
5179 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5180 {
5181   boolean num_checked_players = 0;
5182   int i;
5183
5184   for (i = 0; i < MAX_PLAYERS; i++)
5185   {
5186     if (stored_player[i].active)
5187     {
5188       int sx = stored_player[i].jx;
5189       int sy = stored_player[i].jy;
5190
5191       if (num_checked_players == 0)
5192       {
5193         *sx1 = *sx2 = sx;
5194         *sy1 = *sy2 = sy;
5195       }
5196       else
5197       {
5198         *sx1 = MIN(*sx1, sx);
5199         *sy1 = MIN(*sy1, sy);
5200         *sx2 = MAX(*sx2, sx);
5201         *sy2 = MAX(*sy2, sy);
5202       }
5203
5204       num_checked_players++;
5205     }
5206   }
5207 }
5208
5209 static boolean checkIfAllPlayersFitToScreen_RND(void)
5210 {
5211   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5212
5213   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5214
5215   return (sx2 - sx1 < SCR_FIELDX &&
5216           sy2 - sy1 < SCR_FIELDY);
5217 }
5218
5219 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5220 {
5221   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5222
5223   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5224
5225   *sx = (sx1 + sx2) / 2;
5226   *sy = (sy1 + sy2) / 2;
5227 }
5228
5229 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5230                                boolean center_screen, boolean quick_relocation)
5231 {
5232   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5233   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5234   boolean no_delay = (tape.warp_forward);
5235   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5236   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5237   int new_scroll_x, new_scroll_y;
5238
5239   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5240   {
5241     // case 1: quick relocation inside visible screen (without scrolling)
5242
5243     RedrawPlayfield();
5244
5245     return;
5246   }
5247
5248   if (!level.shifted_relocation || center_screen)
5249   {
5250     // relocation _with_ centering of screen
5251
5252     new_scroll_x = SCROLL_POSITION_X(x);
5253     new_scroll_y = SCROLL_POSITION_Y(y);
5254   }
5255   else
5256   {
5257     // relocation _without_ centering of screen
5258
5259     int center_scroll_x = SCROLL_POSITION_X(old_x);
5260     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5261     int offset_x = x + (scroll_x - center_scroll_x);
5262     int offset_y = y + (scroll_y - center_scroll_y);
5263
5264     // for new screen position, apply previous offset to center position
5265     new_scroll_x = SCROLL_POSITION_X(offset_x);
5266     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5267   }
5268
5269   if (quick_relocation)
5270   {
5271     // case 2: quick relocation (redraw without visible scrolling)
5272
5273     scroll_x = new_scroll_x;
5274     scroll_y = new_scroll_y;
5275
5276     RedrawPlayfield();
5277
5278     return;
5279   }
5280
5281   // case 3: visible relocation (with scrolling to new position)
5282
5283   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5284
5285   SetVideoFrameDelay(wait_delay_value);
5286
5287   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5288   {
5289     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5290     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5291
5292     if (dx == 0 && dy == 0)             // no scrolling needed at all
5293       break;
5294
5295     scroll_x -= dx;
5296     scroll_y -= dy;
5297
5298     // set values for horizontal/vertical screen scrolling (half tile size)
5299     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5300     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5301     int pos_x = dx * TILEX / 2;
5302     int pos_y = dy * TILEY / 2;
5303     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5304     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5305
5306     ScrollLevel(dx, dy);
5307     DrawAllPlayers();
5308
5309     // scroll in two steps of half tile size to make things smoother
5310     BlitScreenToBitmapExt_RND(window, fx, fy);
5311
5312     // scroll second step to align at full tile size
5313     BlitScreenToBitmap(window);
5314   }
5315
5316   DrawAllPlayers();
5317   BackToFront();
5318
5319   SetVideoFrameDelay(frame_delay_value_old);
5320 }
5321
5322 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5323 {
5324   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5325   int player_nr = GET_PLAYER_NR(el_player);
5326   struct PlayerInfo *player = &stored_player[player_nr];
5327   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5328   boolean no_delay = (tape.warp_forward);
5329   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5330   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5331   int old_jx = player->jx;
5332   int old_jy = player->jy;
5333   int old_element = Feld[old_jx][old_jy];
5334   int element = Feld[jx][jy];
5335   boolean player_relocated = (old_jx != jx || old_jy != jy);
5336
5337   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5338   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5339   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5340   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5341   int leave_side_horiz = move_dir_horiz;
5342   int leave_side_vert  = move_dir_vert;
5343   int enter_side = enter_side_horiz | enter_side_vert;
5344   int leave_side = leave_side_horiz | leave_side_vert;
5345
5346   if (player->buried)           // do not reanimate dead player
5347     return;
5348
5349   if (!player_relocated)        // no need to relocate the player
5350     return;
5351
5352   if (IS_PLAYER(jx, jy))        // player already placed at new position
5353   {
5354     RemoveField(jx, jy);        // temporarily remove newly placed player
5355     DrawLevelField(jx, jy);
5356   }
5357
5358   if (player->present)
5359   {
5360     while (player->MovPos)
5361     {
5362       ScrollPlayer(player, SCROLL_GO_ON);
5363       ScrollScreen(NULL, SCROLL_GO_ON);
5364
5365       AdvanceFrameAndPlayerCounters(player->index_nr);
5366
5367       DrawPlayer(player);
5368
5369       BackToFront_WithFrameDelay(wait_delay_value);
5370     }
5371
5372     DrawPlayer(player);         // needed here only to cleanup last field
5373     DrawLevelField(player->jx, player->jy);     // remove player graphic
5374
5375     player->is_moving = FALSE;
5376   }
5377
5378   if (IS_CUSTOM_ELEMENT(old_element))
5379     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5380                                CE_LEFT_BY_PLAYER,
5381                                player->index_bit, leave_side);
5382
5383   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5384                                       CE_PLAYER_LEAVES_X,
5385                                       player->index_bit, leave_side);
5386
5387   Feld[jx][jy] = el_player;
5388   InitPlayerField(jx, jy, el_player, TRUE);
5389
5390   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5391      possible that the relocation target field did not contain a player element,
5392      but a walkable element, to which the new player was relocated -- in this
5393      case, restore that (already initialized!) element on the player field */
5394   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5395   {
5396     Feld[jx][jy] = element;     // restore previously existing element
5397   }
5398
5399   // only visually relocate centered player
5400   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5401                      FALSE, level.instant_relocation);
5402
5403   TestIfPlayerTouchesBadThing(jx, jy);
5404   TestIfPlayerTouchesCustomElement(jx, jy);
5405
5406   if (IS_CUSTOM_ELEMENT(element))
5407     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5408                                player->index_bit, enter_side);
5409
5410   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5411                                       player->index_bit, enter_side);
5412
5413   if (player->is_switching)
5414   {
5415     /* ensure that relocation while still switching an element does not cause
5416        a new element to be treated as also switched directly after relocation
5417        (this is important for teleporter switches that teleport the player to
5418        a place where another teleporter switch is in the same direction, which
5419        would then incorrectly be treated as immediately switched before the
5420        direction key that caused the switch was released) */
5421
5422     player->switch_x += jx - old_jx;
5423     player->switch_y += jy - old_jy;
5424   }
5425 }
5426
5427 static void Explode(int ex, int ey, int phase, int mode)
5428 {
5429   int x, y;
5430   int last_phase;
5431   int border_element;
5432
5433   // !!! eliminate this variable !!!
5434   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5435
5436   if (game.explosions_delayed)
5437   {
5438     ExplodeField[ex][ey] = mode;
5439     return;
5440   }
5441
5442   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5443   {
5444     int center_element = Feld[ex][ey];
5445     int artwork_element, explosion_element;     // set these values later
5446
5447     // remove things displayed in background while burning dynamite
5448     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5449       Back[ex][ey] = 0;
5450
5451     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5452     {
5453       // put moving element to center field (and let it explode there)
5454       center_element = MovingOrBlocked2Element(ex, ey);
5455       RemoveMovingField(ex, ey);
5456       Feld[ex][ey] = center_element;
5457     }
5458
5459     // now "center_element" is finally determined -- set related values now
5460     artwork_element = center_element;           // for custom player artwork
5461     explosion_element = center_element;         // for custom player artwork
5462
5463     if (IS_PLAYER(ex, ey))
5464     {
5465       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5466
5467       artwork_element = stored_player[player_nr].artwork_element;
5468
5469       if (level.use_explosion_element[player_nr])
5470       {
5471         explosion_element = level.explosion_element[player_nr];
5472         artwork_element = explosion_element;
5473       }
5474     }
5475
5476     if (mode == EX_TYPE_NORMAL ||
5477         mode == EX_TYPE_CENTER ||
5478         mode == EX_TYPE_CROSS)
5479       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5480
5481     last_phase = element_info[explosion_element].explosion_delay + 1;
5482
5483     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5484     {
5485       int xx = x - ex + 1;
5486       int yy = y - ey + 1;
5487       int element;
5488
5489       if (!IN_LEV_FIELD(x, y) ||
5490           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5491           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5492         continue;
5493
5494       element = Feld[x][y];
5495
5496       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5497       {
5498         element = MovingOrBlocked2Element(x, y);
5499
5500         if (!IS_EXPLOSION_PROOF(element))
5501           RemoveMovingField(x, y);
5502       }
5503
5504       // indestructible elements can only explode in center (but not flames)
5505       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5506                                            mode == EX_TYPE_BORDER)) ||
5507           element == EL_FLAMES)
5508         continue;
5509
5510       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5511          behaviour, for example when touching a yamyam that explodes to rocks
5512          with active deadly shield, a rock is created under the player !!! */
5513       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5514 #if 0
5515       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5516           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5517            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5518 #else
5519       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5520 #endif
5521       {
5522         if (IS_ACTIVE_BOMB(element))
5523         {
5524           // re-activate things under the bomb like gate or penguin
5525           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5526           Back[x][y] = 0;
5527         }
5528
5529         continue;
5530       }
5531
5532       // save walkable background elements while explosion on same tile
5533       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5534           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5535         Back[x][y] = element;
5536
5537       // ignite explodable elements reached by other explosion
5538       if (element == EL_EXPLOSION)
5539         element = Store2[x][y];
5540
5541       if (AmoebaNr[x][y] &&
5542           (element == EL_AMOEBA_FULL ||
5543            element == EL_BD_AMOEBA ||
5544            element == EL_AMOEBA_GROWING))
5545       {
5546         AmoebaCnt[AmoebaNr[x][y]]--;
5547         AmoebaCnt2[AmoebaNr[x][y]]--;
5548       }
5549
5550       RemoveField(x, y);
5551
5552       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5553       {
5554         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5555
5556         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5557
5558         if (PLAYERINFO(ex, ey)->use_murphy)
5559           Store[x][y] = EL_EMPTY;
5560       }
5561
5562       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5563       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5564       else if (ELEM_IS_PLAYER(center_element))
5565         Store[x][y] = EL_EMPTY;
5566       else if (center_element == EL_YAMYAM)
5567         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5568       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5569         Store[x][y] = element_info[center_element].content.e[xx][yy];
5570 #if 1
5571       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5572       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5573       // otherwise) -- FIX THIS !!!
5574       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5575         Store[x][y] = element_info[element].content.e[1][1];
5576 #else
5577       else if (!CAN_EXPLODE(element))
5578         Store[x][y] = element_info[element].content.e[1][1];
5579 #endif
5580       else
5581         Store[x][y] = EL_EMPTY;
5582
5583       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5584           center_element == EL_AMOEBA_TO_DIAMOND)
5585         Store2[x][y] = element;
5586
5587       Feld[x][y] = EL_EXPLOSION;
5588       GfxElement[x][y] = artwork_element;
5589
5590       ExplodePhase[x][y] = 1;
5591       ExplodeDelay[x][y] = last_phase;
5592
5593       Stop[x][y] = TRUE;
5594     }
5595
5596     if (center_element == EL_YAMYAM)
5597       game.yamyam_content_nr =
5598         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5599
5600     return;
5601   }
5602
5603   if (Stop[ex][ey])
5604     return;
5605
5606   x = ex;
5607   y = ey;
5608
5609   if (phase == 1)
5610     GfxFrame[x][y] = 0;         // restart explosion animation
5611
5612   last_phase = ExplodeDelay[x][y];
5613
5614   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5615
5616   // this can happen if the player leaves an explosion just in time
5617   if (GfxElement[x][y] == EL_UNDEFINED)
5618     GfxElement[x][y] = EL_EMPTY;
5619
5620   border_element = Store2[x][y];
5621   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5622     border_element = StorePlayer[x][y];
5623
5624   if (phase == element_info[border_element].ignition_delay ||
5625       phase == last_phase)
5626   {
5627     boolean border_explosion = FALSE;
5628
5629     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5630         !PLAYER_EXPLOSION_PROTECTED(x, y))
5631     {
5632       KillPlayerUnlessExplosionProtected(x, y);
5633       border_explosion = TRUE;
5634     }
5635     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5636     {
5637       Feld[x][y] = Store2[x][y];
5638       Store2[x][y] = 0;
5639       Bang(x, y);
5640       border_explosion = TRUE;
5641     }
5642     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5643     {
5644       AmoebeUmwandeln(x, y);
5645       Store2[x][y] = 0;
5646       border_explosion = TRUE;
5647     }
5648
5649     // if an element just explodes due to another explosion (chain-reaction),
5650     // do not immediately end the new explosion when it was the last frame of
5651     // the explosion (as it would be done in the following "if"-statement!)
5652     if (border_explosion && phase == last_phase)
5653       return;
5654   }
5655
5656   if (phase == last_phase)
5657   {
5658     int element;
5659
5660     element = Feld[x][y] = Store[x][y];
5661     Store[x][y] = Store2[x][y] = 0;
5662     GfxElement[x][y] = EL_UNDEFINED;
5663
5664     // player can escape from explosions and might therefore be still alive
5665     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5666         element <= EL_PLAYER_IS_EXPLODING_4)
5667     {
5668       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5669       int explosion_element = EL_PLAYER_1 + player_nr;
5670       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5671       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5672
5673       if (level.use_explosion_element[player_nr])
5674         explosion_element = level.explosion_element[player_nr];
5675
5676       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5677                     element_info[explosion_element].content.e[xx][yy]);
5678     }
5679
5680     // restore probably existing indestructible background element
5681     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5682       element = Feld[x][y] = Back[x][y];
5683     Back[x][y] = 0;
5684
5685     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5686     GfxDir[x][y] = MV_NONE;
5687     ChangeDelay[x][y] = 0;
5688     ChangePage[x][y] = -1;
5689
5690     CustomValue[x][y] = 0;
5691
5692     InitField_WithBug2(x, y, FALSE);
5693
5694     TEST_DrawLevelField(x, y);
5695
5696     TestIfElementTouchesCustomElement(x, y);
5697
5698     if (GFX_CRUMBLED(element))
5699       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5700
5701     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5702       StorePlayer[x][y] = 0;
5703
5704     if (ELEM_IS_PLAYER(element))
5705       RelocatePlayer(x, y, element);
5706   }
5707   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5708   {
5709     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5710     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5711
5712     if (phase == delay)
5713       TEST_DrawLevelFieldCrumbled(x, y);
5714
5715     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5716     {
5717       DrawLevelElement(x, y, Back[x][y]);
5718       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5719     }
5720     else if (IS_WALKABLE_UNDER(Back[x][y]))
5721     {
5722       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5723       DrawLevelElementThruMask(x, y, Back[x][y]);
5724     }
5725     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5726       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5727   }
5728 }
5729
5730 static void DynaExplode(int ex, int ey)
5731 {
5732   int i, j;
5733   int dynabomb_element = Feld[ex][ey];
5734   int dynabomb_size = 1;
5735   boolean dynabomb_xl = FALSE;
5736   struct PlayerInfo *player;
5737   static int xy[4][2] =
5738   {
5739     { 0, -1 },
5740     { -1, 0 },
5741     { +1, 0 },
5742     { 0, +1 }
5743   };
5744
5745   if (IS_ACTIVE_BOMB(dynabomb_element))
5746   {
5747     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5748     dynabomb_size = player->dynabomb_size;
5749     dynabomb_xl = player->dynabomb_xl;
5750     player->dynabombs_left++;
5751   }
5752
5753   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5754
5755   for (i = 0; i < NUM_DIRECTIONS; i++)
5756   {
5757     for (j = 1; j <= dynabomb_size; j++)
5758     {
5759       int x = ex + j * xy[i][0];
5760       int y = ey + j * xy[i][1];
5761       int element;
5762
5763       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5764         break;
5765
5766       element = Feld[x][y];
5767
5768       // do not restart explosions of fields with active bombs
5769       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5770         continue;
5771
5772       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5773
5774       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5775           !IS_DIGGABLE(element) && !dynabomb_xl)
5776         break;
5777     }
5778   }
5779 }
5780
5781 void Bang(int x, int y)
5782 {
5783   int element = MovingOrBlocked2Element(x, y);
5784   int explosion_type = EX_TYPE_NORMAL;
5785
5786   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5787   {
5788     struct PlayerInfo *player = PLAYERINFO(x, y);
5789
5790     element = Feld[x][y] = player->initial_element;
5791
5792     if (level.use_explosion_element[player->index_nr])
5793     {
5794       int explosion_element = level.explosion_element[player->index_nr];
5795
5796       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5797         explosion_type = EX_TYPE_CROSS;
5798       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5799         explosion_type = EX_TYPE_CENTER;
5800     }
5801   }
5802
5803   switch (element)
5804   {
5805     case EL_BUG:
5806     case EL_SPACESHIP:
5807     case EL_BD_BUTTERFLY:
5808     case EL_BD_FIREFLY:
5809     case EL_YAMYAM:
5810     case EL_DARK_YAMYAM:
5811     case EL_ROBOT:
5812     case EL_PACMAN:
5813     case EL_MOLE:
5814       RaiseScoreElement(element);
5815       break;
5816
5817     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5818     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5819     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5820     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5821     case EL_DYNABOMB_INCREASE_NUMBER:
5822     case EL_DYNABOMB_INCREASE_SIZE:
5823     case EL_DYNABOMB_INCREASE_POWER:
5824       explosion_type = EX_TYPE_DYNA;
5825       break;
5826
5827     case EL_DC_LANDMINE:
5828       explosion_type = EX_TYPE_CENTER;
5829       break;
5830
5831     case EL_PENGUIN:
5832     case EL_LAMP:
5833     case EL_LAMP_ACTIVE:
5834     case EL_AMOEBA_TO_DIAMOND:
5835       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5836         explosion_type = EX_TYPE_CENTER;
5837       break;
5838
5839     default:
5840       if (element_info[element].explosion_type == EXPLODES_CROSS)
5841         explosion_type = EX_TYPE_CROSS;
5842       else if (element_info[element].explosion_type == EXPLODES_1X1)
5843         explosion_type = EX_TYPE_CENTER;
5844       break;
5845   }
5846
5847   if (explosion_type == EX_TYPE_DYNA)
5848     DynaExplode(x, y);
5849   else
5850     Explode(x, y, EX_PHASE_START, explosion_type);
5851
5852   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5853 }
5854
5855 static void SplashAcid(int x, int y)
5856 {
5857   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5858       (!IN_LEV_FIELD(x - 1, y - 2) ||
5859        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5860     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5861
5862   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5863       (!IN_LEV_FIELD(x + 1, y - 2) ||
5864        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5865     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5866
5867   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5868 }
5869
5870 static void InitBeltMovement(void)
5871 {
5872   static int belt_base_element[4] =
5873   {
5874     EL_CONVEYOR_BELT_1_LEFT,
5875     EL_CONVEYOR_BELT_2_LEFT,
5876     EL_CONVEYOR_BELT_3_LEFT,
5877     EL_CONVEYOR_BELT_4_LEFT
5878   };
5879   static int belt_base_active_element[4] =
5880   {
5881     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5882     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5883     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5884     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5885   };
5886
5887   int x, y, i, j;
5888
5889   // set frame order for belt animation graphic according to belt direction
5890   for (i = 0; i < NUM_BELTS; i++)
5891   {
5892     int belt_nr = i;
5893
5894     for (j = 0; j < NUM_BELT_PARTS; j++)
5895     {
5896       int element = belt_base_active_element[belt_nr] + j;
5897       int graphic_1 = el2img(element);
5898       int graphic_2 = el2panelimg(element);
5899
5900       if (game.belt_dir[i] == MV_LEFT)
5901       {
5902         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5903         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5904       }
5905       else
5906       {
5907         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5908         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5909       }
5910     }
5911   }
5912
5913   SCAN_PLAYFIELD(x, y)
5914   {
5915     int element = Feld[x][y];
5916
5917     for (i = 0; i < NUM_BELTS; i++)
5918     {
5919       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5920       {
5921         int e_belt_nr = getBeltNrFromBeltElement(element);
5922         int belt_nr = i;
5923
5924         if (e_belt_nr == belt_nr)
5925         {
5926           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5927
5928           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5929         }
5930       }
5931     }
5932   }
5933 }
5934
5935 static void ToggleBeltSwitch(int x, int y)
5936 {
5937   static int belt_base_element[4] =
5938   {
5939     EL_CONVEYOR_BELT_1_LEFT,
5940     EL_CONVEYOR_BELT_2_LEFT,
5941     EL_CONVEYOR_BELT_3_LEFT,
5942     EL_CONVEYOR_BELT_4_LEFT
5943   };
5944   static int belt_base_active_element[4] =
5945   {
5946     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5947     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5948     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5949     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5950   };
5951   static int belt_base_switch_element[4] =
5952   {
5953     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5954     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5955     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5956     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5957   };
5958   static int belt_move_dir[4] =
5959   {
5960     MV_LEFT,
5961     MV_NONE,
5962     MV_RIGHT,
5963     MV_NONE,
5964   };
5965
5966   int element = Feld[x][y];
5967   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5968   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5969   int belt_dir = belt_move_dir[belt_dir_nr];
5970   int xx, yy, i;
5971
5972   if (!IS_BELT_SWITCH(element))
5973     return;
5974
5975   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5976   game.belt_dir[belt_nr] = belt_dir;
5977
5978   if (belt_dir_nr == 3)
5979     belt_dir_nr = 1;
5980
5981   // set frame order for belt animation graphic according to belt direction
5982   for (i = 0; i < NUM_BELT_PARTS; i++)
5983   {
5984     int element = belt_base_active_element[belt_nr] + i;
5985     int graphic_1 = el2img(element);
5986     int graphic_2 = el2panelimg(element);
5987
5988     if (belt_dir == MV_LEFT)
5989     {
5990       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5991       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5992     }
5993     else
5994     {
5995       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5996       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5997     }
5998   }
5999
6000   SCAN_PLAYFIELD(xx, yy)
6001   {
6002     int element = Feld[xx][yy];
6003
6004     if (IS_BELT_SWITCH(element))
6005     {
6006       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6007
6008       if (e_belt_nr == belt_nr)
6009       {
6010         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6011         TEST_DrawLevelField(xx, yy);
6012       }
6013     }
6014     else if (IS_BELT(element) && belt_dir != MV_NONE)
6015     {
6016       int e_belt_nr = getBeltNrFromBeltElement(element);
6017
6018       if (e_belt_nr == belt_nr)
6019       {
6020         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6021
6022         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6023         TEST_DrawLevelField(xx, yy);
6024       }
6025     }
6026     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6027     {
6028       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6029
6030       if (e_belt_nr == belt_nr)
6031       {
6032         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6033
6034         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6035         TEST_DrawLevelField(xx, yy);
6036       }
6037     }
6038   }
6039 }
6040
6041 static void ToggleSwitchgateSwitch(int x, int y)
6042 {
6043   int xx, yy;
6044
6045   game.switchgate_pos = !game.switchgate_pos;
6046
6047   SCAN_PLAYFIELD(xx, yy)
6048   {
6049     int element = Feld[xx][yy];
6050
6051     if (element == EL_SWITCHGATE_SWITCH_UP)
6052     {
6053       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6054       TEST_DrawLevelField(xx, yy);
6055     }
6056     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6057     {
6058       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6059       TEST_DrawLevelField(xx, yy);
6060     }
6061     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6062     {
6063       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6064       TEST_DrawLevelField(xx, yy);
6065     }
6066     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6067     {
6068       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6069       TEST_DrawLevelField(xx, yy);
6070     }
6071     else if (element == EL_SWITCHGATE_OPEN ||
6072              element == EL_SWITCHGATE_OPENING)
6073     {
6074       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6075
6076       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6077     }
6078     else if (element == EL_SWITCHGATE_CLOSED ||
6079              element == EL_SWITCHGATE_CLOSING)
6080     {
6081       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6082
6083       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6084     }
6085   }
6086 }
6087
6088 static int getInvisibleActiveFromInvisibleElement(int element)
6089 {
6090   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6091           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6092           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6093           element);
6094 }
6095
6096 static int getInvisibleFromInvisibleActiveElement(int element)
6097 {
6098   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6099           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6100           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6101           element);
6102 }
6103
6104 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6105 {
6106   int x, y;
6107
6108   SCAN_PLAYFIELD(x, y)
6109   {
6110     int element = Feld[x][y];
6111
6112     if (element == EL_LIGHT_SWITCH &&
6113         game.light_time_left > 0)
6114     {
6115       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6116       TEST_DrawLevelField(x, y);
6117     }
6118     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6119              game.light_time_left == 0)
6120     {
6121       Feld[x][y] = EL_LIGHT_SWITCH;
6122       TEST_DrawLevelField(x, y);
6123     }
6124     else if (element == EL_EMC_DRIPPER &&
6125              game.light_time_left > 0)
6126     {
6127       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6128       TEST_DrawLevelField(x, y);
6129     }
6130     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6131              game.light_time_left == 0)
6132     {
6133       Feld[x][y] = EL_EMC_DRIPPER;
6134       TEST_DrawLevelField(x, y);
6135     }
6136     else if (element == EL_INVISIBLE_STEELWALL ||
6137              element == EL_INVISIBLE_WALL ||
6138              element == EL_INVISIBLE_SAND)
6139     {
6140       if (game.light_time_left > 0)
6141         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6142
6143       TEST_DrawLevelField(x, y);
6144
6145       // uncrumble neighbour fields, if needed
6146       if (element == EL_INVISIBLE_SAND)
6147         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6148     }
6149     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6150              element == EL_INVISIBLE_WALL_ACTIVE ||
6151              element == EL_INVISIBLE_SAND_ACTIVE)
6152     {
6153       if (game.light_time_left == 0)
6154         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6155
6156       TEST_DrawLevelField(x, y);
6157
6158       // re-crumble neighbour fields, if needed
6159       if (element == EL_INVISIBLE_SAND)
6160         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6161     }
6162   }
6163 }
6164
6165 static void RedrawAllInvisibleElementsForLenses(void)
6166 {
6167   int x, y;
6168
6169   SCAN_PLAYFIELD(x, y)
6170   {
6171     int element = Feld[x][y];
6172
6173     if (element == EL_EMC_DRIPPER &&
6174         game.lenses_time_left > 0)
6175     {
6176       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6177       TEST_DrawLevelField(x, y);
6178     }
6179     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6180              game.lenses_time_left == 0)
6181     {
6182       Feld[x][y] = EL_EMC_DRIPPER;
6183       TEST_DrawLevelField(x, y);
6184     }
6185     else if (element == EL_INVISIBLE_STEELWALL ||
6186              element == EL_INVISIBLE_WALL ||
6187              element == EL_INVISIBLE_SAND)
6188     {
6189       if (game.lenses_time_left > 0)
6190         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6191
6192       TEST_DrawLevelField(x, y);
6193
6194       // uncrumble neighbour fields, if needed
6195       if (element == EL_INVISIBLE_SAND)
6196         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6197     }
6198     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6199              element == EL_INVISIBLE_WALL_ACTIVE ||
6200              element == EL_INVISIBLE_SAND_ACTIVE)
6201     {
6202       if (game.lenses_time_left == 0)
6203         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6204
6205       TEST_DrawLevelField(x, y);
6206
6207       // re-crumble neighbour fields, if needed
6208       if (element == EL_INVISIBLE_SAND)
6209         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6210     }
6211   }
6212 }
6213
6214 static void RedrawAllInvisibleElementsForMagnifier(void)
6215 {
6216   int x, y;
6217
6218   SCAN_PLAYFIELD(x, y)
6219   {
6220     int element = Feld[x][y];
6221
6222     if (element == EL_EMC_FAKE_GRASS &&
6223         game.magnify_time_left > 0)
6224     {
6225       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6226       TEST_DrawLevelField(x, y);
6227     }
6228     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6229              game.magnify_time_left == 0)
6230     {
6231       Feld[x][y] = EL_EMC_FAKE_GRASS;
6232       TEST_DrawLevelField(x, y);
6233     }
6234     else if (IS_GATE_GRAY(element) &&
6235              game.magnify_time_left > 0)
6236     {
6237       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6238                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6239                     IS_EM_GATE_GRAY(element) ?
6240                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6241                     IS_EMC_GATE_GRAY(element) ?
6242                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6243                     IS_DC_GATE_GRAY(element) ?
6244                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6245                     element);
6246       TEST_DrawLevelField(x, y);
6247     }
6248     else if (IS_GATE_GRAY_ACTIVE(element) &&
6249              game.magnify_time_left == 0)
6250     {
6251       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6252                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6253                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6254                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6255                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6256                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6257                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6258                     EL_DC_GATE_WHITE_GRAY :
6259                     element);
6260       TEST_DrawLevelField(x, y);
6261     }
6262   }
6263 }
6264
6265 static void ToggleLightSwitch(int x, int y)
6266 {
6267   int element = Feld[x][y];
6268
6269   game.light_time_left =
6270     (element == EL_LIGHT_SWITCH ?
6271      level.time_light * FRAMES_PER_SECOND : 0);
6272
6273   RedrawAllLightSwitchesAndInvisibleElements();
6274 }
6275
6276 static void ActivateTimegateSwitch(int x, int y)
6277 {
6278   int xx, yy;
6279
6280   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6281
6282   SCAN_PLAYFIELD(xx, yy)
6283   {
6284     int element = Feld[xx][yy];
6285
6286     if (element == EL_TIMEGATE_CLOSED ||
6287         element == EL_TIMEGATE_CLOSING)
6288     {
6289       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6290       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6291     }
6292
6293     /*
6294     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6295     {
6296       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6297       TEST_DrawLevelField(xx, yy);
6298     }
6299     */
6300
6301   }
6302
6303   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6304                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6305 }
6306
6307 static void Impact(int x, int y)
6308 {
6309   boolean last_line = (y == lev_fieldy - 1);
6310   boolean object_hit = FALSE;
6311   boolean impact = (last_line || object_hit);
6312   int element = Feld[x][y];
6313   int smashed = EL_STEELWALL;
6314
6315   if (!last_line)       // check if element below was hit
6316   {
6317     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6318       return;
6319
6320     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6321                                          MovDir[x][y + 1] != MV_DOWN ||
6322                                          MovPos[x][y + 1] <= TILEY / 2));
6323
6324     // do not smash moving elements that left the smashed field in time
6325     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6326         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6327       object_hit = FALSE;
6328
6329 #if USE_QUICKSAND_IMPACT_BUGFIX
6330     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6331     {
6332       RemoveMovingField(x, y + 1);
6333       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6334       Feld[x][y + 2] = EL_ROCK;
6335       TEST_DrawLevelField(x, y + 2);
6336
6337       object_hit = TRUE;
6338     }
6339
6340     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6341     {
6342       RemoveMovingField(x, y + 1);
6343       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6344       Feld[x][y + 2] = EL_ROCK;
6345       TEST_DrawLevelField(x, y + 2);
6346
6347       object_hit = TRUE;
6348     }
6349 #endif
6350
6351     if (object_hit)
6352       smashed = MovingOrBlocked2Element(x, y + 1);
6353
6354     impact = (last_line || object_hit);
6355   }
6356
6357   if (!last_line && smashed == EL_ACID) // element falls into acid
6358   {
6359     SplashAcid(x, y + 1);
6360     return;
6361   }
6362
6363   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6364   // only reset graphic animation if graphic really changes after impact
6365   if (impact &&
6366       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6367   {
6368     ResetGfxAnimation(x, y);
6369     TEST_DrawLevelField(x, y);
6370   }
6371
6372   if (impact && CAN_EXPLODE_IMPACT(element))
6373   {
6374     Bang(x, y);
6375     return;
6376   }
6377   else if (impact && element == EL_PEARL &&
6378            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6379   {
6380     ResetGfxAnimation(x, y);
6381
6382     Feld[x][y] = EL_PEARL_BREAKING;
6383     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6384     return;
6385   }
6386   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6387   {
6388     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6389
6390     return;
6391   }
6392
6393   if (impact && element == EL_AMOEBA_DROP)
6394   {
6395     if (object_hit && IS_PLAYER(x, y + 1))
6396       KillPlayerUnlessEnemyProtected(x, y + 1);
6397     else if (object_hit && smashed == EL_PENGUIN)
6398       Bang(x, y + 1);
6399     else
6400     {
6401       Feld[x][y] = EL_AMOEBA_GROWING;
6402       Store[x][y] = EL_AMOEBA_WET;
6403
6404       ResetRandomAnimationValue(x, y);
6405     }
6406     return;
6407   }
6408
6409   if (object_hit)               // check which object was hit
6410   {
6411     if ((CAN_PASS_MAGIC_WALL(element) && 
6412          (smashed == EL_MAGIC_WALL ||
6413           smashed == EL_BD_MAGIC_WALL)) ||
6414         (CAN_PASS_DC_MAGIC_WALL(element) &&
6415          smashed == EL_DC_MAGIC_WALL))
6416     {
6417       int xx, yy;
6418       int activated_magic_wall =
6419         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6420          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6421          EL_DC_MAGIC_WALL_ACTIVE);
6422
6423       // activate magic wall / mill
6424       SCAN_PLAYFIELD(xx, yy)
6425       {
6426         if (Feld[xx][yy] == smashed)
6427           Feld[xx][yy] = activated_magic_wall;
6428       }
6429
6430       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6431       game.magic_wall_active = TRUE;
6432
6433       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6434                             SND_MAGIC_WALL_ACTIVATING :
6435                             smashed == EL_BD_MAGIC_WALL ?
6436                             SND_BD_MAGIC_WALL_ACTIVATING :
6437                             SND_DC_MAGIC_WALL_ACTIVATING));
6438     }
6439
6440     if (IS_PLAYER(x, y + 1))
6441     {
6442       if (CAN_SMASH_PLAYER(element))
6443       {
6444         KillPlayerUnlessEnemyProtected(x, y + 1);
6445         return;
6446       }
6447     }
6448     else if (smashed == EL_PENGUIN)
6449     {
6450       if (CAN_SMASH_PLAYER(element))
6451       {
6452         Bang(x, y + 1);
6453         return;
6454       }
6455     }
6456     else if (element == EL_BD_DIAMOND)
6457     {
6458       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6459       {
6460         Bang(x, y + 1);
6461         return;
6462       }
6463     }
6464     else if (((element == EL_SP_INFOTRON ||
6465                element == EL_SP_ZONK) &&
6466               (smashed == EL_SP_SNIKSNAK ||
6467                smashed == EL_SP_ELECTRON ||
6468                smashed == EL_SP_DISK_ORANGE)) ||
6469              (element == EL_SP_INFOTRON &&
6470               smashed == EL_SP_DISK_YELLOW))
6471     {
6472       Bang(x, y + 1);
6473       return;
6474     }
6475     else if (CAN_SMASH_EVERYTHING(element))
6476     {
6477       if (IS_CLASSIC_ENEMY(smashed) ||
6478           CAN_EXPLODE_SMASHED(smashed))
6479       {
6480         Bang(x, y + 1);
6481         return;
6482       }
6483       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6484       {
6485         if (smashed == EL_LAMP ||
6486             smashed == EL_LAMP_ACTIVE)
6487         {
6488           Bang(x, y + 1);
6489           return;
6490         }
6491         else if (smashed == EL_NUT)
6492         {
6493           Feld[x][y + 1] = EL_NUT_BREAKING;
6494           PlayLevelSound(x, y, SND_NUT_BREAKING);
6495           RaiseScoreElement(EL_NUT);
6496           return;
6497         }
6498         else if (smashed == EL_PEARL)
6499         {
6500           ResetGfxAnimation(x, y);
6501
6502           Feld[x][y + 1] = EL_PEARL_BREAKING;
6503           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6504           return;
6505         }
6506         else if (smashed == EL_DIAMOND)
6507         {
6508           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6509           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6510           return;
6511         }
6512         else if (IS_BELT_SWITCH(smashed))
6513         {
6514           ToggleBeltSwitch(x, y + 1);
6515         }
6516         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6517                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6518                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6519                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6520         {
6521           ToggleSwitchgateSwitch(x, y + 1);
6522         }
6523         else if (smashed == EL_LIGHT_SWITCH ||
6524                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6525         {
6526           ToggleLightSwitch(x, y + 1);
6527         }
6528         else
6529         {
6530           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6531
6532           CheckElementChangeBySide(x, y + 1, smashed, element,
6533                                    CE_SWITCHED, CH_SIDE_TOP);
6534           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6535                                             CH_SIDE_TOP);
6536         }
6537       }
6538       else
6539       {
6540         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6541       }
6542     }
6543   }
6544
6545   // play sound of magic wall / mill
6546   if (!last_line &&
6547       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6548        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6549        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6550   {
6551     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6552       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6553     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6554       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6555     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6556       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6557
6558     return;
6559   }
6560
6561   // play sound of object that hits the ground
6562   if (last_line || object_hit)
6563     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6564 }
6565
6566 static void TurnRoundExt(int x, int y)
6567 {
6568   static struct
6569   {
6570     int dx, dy;
6571   } move_xy[] =
6572   {
6573     {  0,  0 },
6574     { -1,  0 },
6575     { +1,  0 },
6576     {  0,  0 },
6577     {  0, -1 },
6578     {  0,  0 }, { 0, 0 }, { 0, 0 },
6579     {  0, +1 }
6580   };
6581   static struct
6582   {
6583     int left, right, back;
6584   } turn[] =
6585   {
6586     { 0,        0,              0        },
6587     { MV_DOWN,  MV_UP,          MV_RIGHT },
6588     { MV_UP,    MV_DOWN,        MV_LEFT  },
6589     { 0,        0,              0        },
6590     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6591     { 0,        0,              0        },
6592     { 0,        0,              0        },
6593     { 0,        0,              0        },
6594     { MV_RIGHT, MV_LEFT,        MV_UP    }
6595   };
6596
6597   int element = Feld[x][y];
6598   int move_pattern = element_info[element].move_pattern;
6599
6600   int old_move_dir = MovDir[x][y];
6601   int left_dir  = turn[old_move_dir].left;
6602   int right_dir = turn[old_move_dir].right;
6603   int back_dir  = turn[old_move_dir].back;
6604
6605   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6606   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6607   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6608   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6609
6610   int left_x  = x + left_dx,  left_y  = y + left_dy;
6611   int right_x = x + right_dx, right_y = y + right_dy;
6612   int move_x  = x + move_dx,  move_y  = y + move_dy;
6613
6614   int xx, yy;
6615
6616   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6617   {
6618     TestIfBadThingTouchesOtherBadThing(x, y);
6619
6620     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6621       MovDir[x][y] = right_dir;
6622     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6623       MovDir[x][y] = left_dir;
6624
6625     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6626       MovDelay[x][y] = 9;
6627     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6628       MovDelay[x][y] = 1;
6629   }
6630   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6631   {
6632     TestIfBadThingTouchesOtherBadThing(x, y);
6633
6634     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6635       MovDir[x][y] = left_dir;
6636     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6637       MovDir[x][y] = right_dir;
6638
6639     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6640       MovDelay[x][y] = 9;
6641     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6642       MovDelay[x][y] = 1;
6643   }
6644   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6645   {
6646     TestIfBadThingTouchesOtherBadThing(x, y);
6647
6648     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6649       MovDir[x][y] = left_dir;
6650     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6651       MovDir[x][y] = right_dir;
6652
6653     if (MovDir[x][y] != old_move_dir)
6654       MovDelay[x][y] = 9;
6655   }
6656   else if (element == EL_YAMYAM)
6657   {
6658     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6659     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6660
6661     if (can_turn_left && can_turn_right)
6662       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6663     else if (can_turn_left)
6664       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6665     else if (can_turn_right)
6666       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6667     else
6668       MovDir[x][y] = back_dir;
6669
6670     MovDelay[x][y] = 16 + 16 * RND(3);
6671   }
6672   else if (element == EL_DARK_YAMYAM)
6673   {
6674     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6675                                                          left_x, left_y);
6676     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6677                                                          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] = 16 + 16 * RND(3);
6689   }
6690   else if (element == EL_PACMAN)
6691   {
6692     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6693     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6694
6695     if (can_turn_left && can_turn_right)
6696       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6697     else if (can_turn_left)
6698       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6699     else if (can_turn_right)
6700       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6701     else
6702       MovDir[x][y] = back_dir;
6703
6704     MovDelay[x][y] = 6 + RND(40);
6705   }
6706   else if (element == EL_PIG)
6707   {
6708     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6709     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6710     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6711     boolean should_turn_left, should_turn_right, should_move_on;
6712     int rnd_value = 24;
6713     int rnd = RND(rnd_value);
6714
6715     should_turn_left = (can_turn_left &&
6716                         (!can_move_on ||
6717                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6718                                                    y + back_dy + left_dy)));
6719     should_turn_right = (can_turn_right &&
6720                          (!can_move_on ||
6721                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6722                                                     y + back_dy + right_dy)));
6723     should_move_on = (can_move_on &&
6724                       (!can_turn_left ||
6725                        !can_turn_right ||
6726                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6727                                                  y + move_dy + left_dy) ||
6728                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6729                                                  y + move_dy + right_dy)));
6730
6731     if (should_turn_left || should_turn_right || should_move_on)
6732     {
6733       if (should_turn_left && should_turn_right && should_move_on)
6734         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6735                         rnd < 2 * rnd_value / 3 ? right_dir :
6736                         old_move_dir);
6737       else if (should_turn_left && should_turn_right)
6738         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6739       else if (should_turn_left && should_move_on)
6740         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6741       else if (should_turn_right && should_move_on)
6742         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6743       else if (should_turn_left)
6744         MovDir[x][y] = left_dir;
6745       else if (should_turn_right)
6746         MovDir[x][y] = right_dir;
6747       else if (should_move_on)
6748         MovDir[x][y] = old_move_dir;
6749     }
6750     else if (can_move_on && rnd > rnd_value / 8)
6751       MovDir[x][y] = old_move_dir;
6752     else if (can_turn_left && can_turn_right)
6753       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6754     else if (can_turn_left && rnd > rnd_value / 8)
6755       MovDir[x][y] = left_dir;
6756     else if (can_turn_right && rnd > rnd_value/8)
6757       MovDir[x][y] = right_dir;
6758     else
6759       MovDir[x][y] = back_dir;
6760
6761     xx = x + move_xy[MovDir[x][y]].dx;
6762     yy = y + move_xy[MovDir[x][y]].dy;
6763
6764     if (!IN_LEV_FIELD(xx, yy) ||
6765         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6766       MovDir[x][y] = old_move_dir;
6767
6768     MovDelay[x][y] = 0;
6769   }
6770   else if (element == EL_DRAGON)
6771   {
6772     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6773     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6774     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6775     int rnd_value = 24;
6776     int rnd = RND(rnd_value);
6777
6778     if (can_move_on && rnd > rnd_value / 8)
6779       MovDir[x][y] = old_move_dir;
6780     else if (can_turn_left && can_turn_right)
6781       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6782     else if (can_turn_left && rnd > rnd_value / 8)
6783       MovDir[x][y] = left_dir;
6784     else if (can_turn_right && rnd > rnd_value / 8)
6785       MovDir[x][y] = right_dir;
6786     else
6787       MovDir[x][y] = back_dir;
6788
6789     xx = x + move_xy[MovDir[x][y]].dx;
6790     yy = y + move_xy[MovDir[x][y]].dy;
6791
6792     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6793       MovDir[x][y] = old_move_dir;
6794
6795     MovDelay[x][y] = 0;
6796   }
6797   else if (element == EL_MOLE)
6798   {
6799     boolean can_move_on =
6800       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6801                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6802                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6803     if (!can_move_on)
6804     {
6805       boolean can_turn_left =
6806         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6807                               IS_AMOEBOID(Feld[left_x][left_y])));
6808
6809       boolean can_turn_right =
6810         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6811                               IS_AMOEBOID(Feld[right_x][right_y])));
6812
6813       if (can_turn_left && can_turn_right)
6814         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6815       else if (can_turn_left)
6816         MovDir[x][y] = left_dir;
6817       else
6818         MovDir[x][y] = right_dir;
6819     }
6820
6821     if (MovDir[x][y] != old_move_dir)
6822       MovDelay[x][y] = 9;
6823   }
6824   else if (element == EL_BALLOON)
6825   {
6826     MovDir[x][y] = game.wind_direction;
6827     MovDelay[x][y] = 0;
6828   }
6829   else if (element == EL_SPRING)
6830   {
6831     if (MovDir[x][y] & MV_HORIZONTAL)
6832     {
6833       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6834           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6835       {
6836         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6837         ResetGfxAnimation(move_x, move_y);
6838         TEST_DrawLevelField(move_x, move_y);
6839
6840         MovDir[x][y] = back_dir;
6841       }
6842       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6843                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6844         MovDir[x][y] = MV_NONE;
6845     }
6846
6847     MovDelay[x][y] = 0;
6848   }
6849   else if (element == EL_ROBOT ||
6850            element == EL_SATELLITE ||
6851            element == EL_PENGUIN ||
6852            element == EL_EMC_ANDROID)
6853   {
6854     int attr_x = -1, attr_y = -1;
6855
6856     if (game.all_players_gone)
6857     {
6858       attr_x = game.exit_x;
6859       attr_y = game.exit_y;
6860     }
6861     else
6862     {
6863       int i;
6864
6865       for (i = 0; i < MAX_PLAYERS; i++)
6866       {
6867         struct PlayerInfo *player = &stored_player[i];
6868         int jx = player->jx, jy = player->jy;
6869
6870         if (!player->active)
6871           continue;
6872
6873         if (attr_x == -1 ||
6874             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6875         {
6876           attr_x = jx;
6877           attr_y = jy;
6878         }
6879       }
6880     }
6881
6882     if (element == EL_ROBOT &&
6883         game.robot_wheel_x >= 0 &&
6884         game.robot_wheel_y >= 0 &&
6885         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6886          game.engine_version < VERSION_IDENT(3,1,0,0)))
6887     {
6888       attr_x = game.robot_wheel_x;
6889       attr_y = game.robot_wheel_y;
6890     }
6891
6892     if (element == EL_PENGUIN)
6893     {
6894       int i;
6895       static int xy[4][2] =
6896       {
6897         { 0, -1 },
6898         { -1, 0 },
6899         { +1, 0 },
6900         { 0, +1 }
6901       };
6902
6903       for (i = 0; i < NUM_DIRECTIONS; i++)
6904       {
6905         int ex = x + xy[i][0];
6906         int ey = y + xy[i][1];
6907
6908         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6909                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6910                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6911                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6912         {
6913           attr_x = ex;
6914           attr_y = ey;
6915           break;
6916         }
6917       }
6918     }
6919
6920     MovDir[x][y] = MV_NONE;
6921     if (attr_x < x)
6922       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6923     else if (attr_x > x)
6924       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6925     if (attr_y < y)
6926       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6927     else if (attr_y > y)
6928       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6929
6930     if (element == EL_ROBOT)
6931     {
6932       int newx, newy;
6933
6934       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6935         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6936       Moving2Blocked(x, y, &newx, &newy);
6937
6938       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6939         MovDelay[x][y] = 8 + 8 * !RND(3);
6940       else
6941         MovDelay[x][y] = 16;
6942     }
6943     else if (element == EL_PENGUIN)
6944     {
6945       int newx, newy;
6946
6947       MovDelay[x][y] = 1;
6948
6949       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6950       {
6951         boolean first_horiz = RND(2);
6952         int new_move_dir = MovDir[x][y];
6953
6954         MovDir[x][y] =
6955           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6956         Moving2Blocked(x, y, &newx, &newy);
6957
6958         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6959           return;
6960
6961         MovDir[x][y] =
6962           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6963         Moving2Blocked(x, y, &newx, &newy);
6964
6965         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6966           return;
6967
6968         MovDir[x][y] = old_move_dir;
6969         return;
6970       }
6971     }
6972     else if (element == EL_SATELLITE)
6973     {
6974       int newx, newy;
6975
6976       MovDelay[x][y] = 1;
6977
6978       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6979       {
6980         boolean first_horiz = RND(2);
6981         int new_move_dir = MovDir[x][y];
6982
6983         MovDir[x][y] =
6984           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6985         Moving2Blocked(x, y, &newx, &newy);
6986
6987         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6988           return;
6989
6990         MovDir[x][y] =
6991           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6992         Moving2Blocked(x, y, &newx, &newy);
6993
6994         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6995           return;
6996
6997         MovDir[x][y] = old_move_dir;
6998         return;
6999       }
7000     }
7001     else if (element == EL_EMC_ANDROID)
7002     {
7003       static int check_pos[16] =
7004       {
7005         -1,             //  0 => (invalid)
7006         7,              //  1 => MV_LEFT
7007         3,              //  2 => MV_RIGHT
7008         -1,             //  3 => (invalid)
7009         1,              //  4 =>            MV_UP
7010         0,              //  5 => MV_LEFT  | MV_UP
7011         2,              //  6 => MV_RIGHT | MV_UP
7012         -1,             //  7 => (invalid)
7013         5,              //  8 =>            MV_DOWN
7014         6,              //  9 => MV_LEFT  | MV_DOWN
7015         4,              // 10 => MV_RIGHT | MV_DOWN
7016         -1,             // 11 => (invalid)
7017         -1,             // 12 => (invalid)
7018         -1,             // 13 => (invalid)
7019         -1,             // 14 => (invalid)
7020         -1,             // 15 => (invalid)
7021       };
7022       static struct
7023       {
7024         int dx, dy;
7025         int dir;
7026       } check_xy[8] =
7027       {
7028         { -1, -1,       MV_LEFT  | MV_UP   },
7029         {  0, -1,                  MV_UP   },
7030         { +1, -1,       MV_RIGHT | MV_UP   },
7031         { +1,  0,       MV_RIGHT           },
7032         { +1, +1,       MV_RIGHT | MV_DOWN },
7033         {  0, +1,                  MV_DOWN },
7034         { -1, +1,       MV_LEFT  | MV_DOWN },
7035         { -1,  0,       MV_LEFT            },
7036       };
7037       int start_pos, check_order;
7038       boolean can_clone = FALSE;
7039       int i;
7040
7041       // check if there is any free field around current position
7042       for (i = 0; i < 8; i++)
7043       {
7044         int newx = x + check_xy[i].dx;
7045         int newy = y + check_xy[i].dy;
7046
7047         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7048         {
7049           can_clone = TRUE;
7050
7051           break;
7052         }
7053       }
7054
7055       if (can_clone)            // randomly find an element to clone
7056       {
7057         can_clone = FALSE;
7058
7059         start_pos = check_pos[RND(8)];
7060         check_order = (RND(2) ? -1 : +1);
7061
7062         for (i = 0; i < 8; i++)
7063         {
7064           int pos_raw = start_pos + i * check_order;
7065           int pos = (pos_raw + 8) % 8;
7066           int newx = x + check_xy[pos].dx;
7067           int newy = y + check_xy[pos].dy;
7068
7069           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7070           {
7071             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7072             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7073
7074             Store[x][y] = Feld[newx][newy];
7075
7076             can_clone = TRUE;
7077
7078             break;
7079           }
7080         }
7081       }
7082
7083       if (can_clone)            // randomly find a direction to move
7084       {
7085         can_clone = FALSE;
7086
7087         start_pos = check_pos[RND(8)];
7088         check_order = (RND(2) ? -1 : +1);
7089
7090         for (i = 0; i < 8; i++)
7091         {
7092           int pos_raw = start_pos + i * check_order;
7093           int pos = (pos_raw + 8) % 8;
7094           int newx = x + check_xy[pos].dx;
7095           int newy = y + check_xy[pos].dy;
7096           int new_move_dir = check_xy[pos].dir;
7097
7098           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7099           {
7100             MovDir[x][y] = new_move_dir;
7101             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7102
7103             can_clone = TRUE;
7104
7105             break;
7106           }
7107         }
7108       }
7109
7110       if (can_clone)            // cloning and moving successful
7111         return;
7112
7113       // cannot clone -- try to move towards player
7114
7115       start_pos = check_pos[MovDir[x][y] & 0x0f];
7116       check_order = (RND(2) ? -1 : +1);
7117
7118       for (i = 0; i < 3; i++)
7119       {
7120         // first check start_pos, then previous/next or (next/previous) pos
7121         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7122         int pos = (pos_raw + 8) % 8;
7123         int newx = x + check_xy[pos].dx;
7124         int newy = y + check_xy[pos].dy;
7125         int new_move_dir = check_xy[pos].dir;
7126
7127         if (IS_PLAYER(newx, newy))
7128           break;
7129
7130         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7131         {
7132           MovDir[x][y] = new_move_dir;
7133           MovDelay[x][y] = level.android_move_time * 8 + 1;
7134
7135           break;
7136         }
7137       }
7138     }
7139   }
7140   else if (move_pattern == MV_TURNING_LEFT ||
7141            move_pattern == MV_TURNING_RIGHT ||
7142            move_pattern == MV_TURNING_LEFT_RIGHT ||
7143            move_pattern == MV_TURNING_RIGHT_LEFT ||
7144            move_pattern == MV_TURNING_RANDOM ||
7145            move_pattern == MV_ALL_DIRECTIONS)
7146   {
7147     boolean can_turn_left =
7148       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7149     boolean can_turn_right =
7150       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7151
7152     if (element_info[element].move_stepsize == 0)       // "not moving"
7153       return;
7154
7155     if (move_pattern == MV_TURNING_LEFT)
7156       MovDir[x][y] = left_dir;
7157     else if (move_pattern == MV_TURNING_RIGHT)
7158       MovDir[x][y] = right_dir;
7159     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7160       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7161     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7162       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7163     else if (move_pattern == MV_TURNING_RANDOM)
7164       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7165                       can_turn_right && !can_turn_left ? right_dir :
7166                       RND(2) ? left_dir : right_dir);
7167     else if (can_turn_left && can_turn_right)
7168       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7169     else if (can_turn_left)
7170       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7171     else if (can_turn_right)
7172       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7173     else
7174       MovDir[x][y] = back_dir;
7175
7176     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7177   }
7178   else if (move_pattern == MV_HORIZONTAL ||
7179            move_pattern == MV_VERTICAL)
7180   {
7181     if (move_pattern & old_move_dir)
7182       MovDir[x][y] = back_dir;
7183     else if (move_pattern == MV_HORIZONTAL)
7184       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7185     else if (move_pattern == MV_VERTICAL)
7186       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7187
7188     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7189   }
7190   else if (move_pattern & MV_ANY_DIRECTION)
7191   {
7192     MovDir[x][y] = move_pattern;
7193     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7194   }
7195   else if (move_pattern & MV_WIND_DIRECTION)
7196   {
7197     MovDir[x][y] = game.wind_direction;
7198     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7199   }
7200   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7201   {
7202     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7203       MovDir[x][y] = left_dir;
7204     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7205       MovDir[x][y] = right_dir;
7206
7207     if (MovDir[x][y] != old_move_dir)
7208       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7209   }
7210   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7211   {
7212     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7213       MovDir[x][y] = right_dir;
7214     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7215       MovDir[x][y] = left_dir;
7216
7217     if (MovDir[x][y] != old_move_dir)
7218       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7219   }
7220   else if (move_pattern == MV_TOWARDS_PLAYER ||
7221            move_pattern == MV_AWAY_FROM_PLAYER)
7222   {
7223     int attr_x = -1, attr_y = -1;
7224     int newx, newy;
7225     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7226
7227     if (game.all_players_gone)
7228     {
7229       attr_x = game.exit_x;
7230       attr_y = game.exit_y;
7231     }
7232     else
7233     {
7234       int i;
7235
7236       for (i = 0; i < MAX_PLAYERS; i++)
7237       {
7238         struct PlayerInfo *player = &stored_player[i];
7239         int jx = player->jx, jy = player->jy;
7240
7241         if (!player->active)
7242           continue;
7243
7244         if (attr_x == -1 ||
7245             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7246         {
7247           attr_x = jx;
7248           attr_y = jy;
7249         }
7250       }
7251     }
7252
7253     MovDir[x][y] = MV_NONE;
7254     if (attr_x < x)
7255       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7256     else if (attr_x > x)
7257       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7258     if (attr_y < y)
7259       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7260     else if (attr_y > y)
7261       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7262
7263     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7264
7265     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7266     {
7267       boolean first_horiz = RND(2);
7268       int new_move_dir = MovDir[x][y];
7269
7270       if (element_info[element].move_stepsize == 0)     // "not moving"
7271       {
7272         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7273         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7274
7275         return;
7276       }
7277
7278       MovDir[x][y] =
7279         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7280       Moving2Blocked(x, y, &newx, &newy);
7281
7282       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7283         return;
7284
7285       MovDir[x][y] =
7286         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7287       Moving2Blocked(x, y, &newx, &newy);
7288
7289       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7290         return;
7291
7292       MovDir[x][y] = old_move_dir;
7293     }
7294   }
7295   else if (move_pattern == MV_WHEN_PUSHED ||
7296            move_pattern == MV_WHEN_DROPPED)
7297   {
7298     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7299       MovDir[x][y] = MV_NONE;
7300
7301     MovDelay[x][y] = 0;
7302   }
7303   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7304   {
7305     static int test_xy[7][2] =
7306     {
7307       { 0, -1 },
7308       { -1, 0 },
7309       { +1, 0 },
7310       { 0, +1 },
7311       { 0, -1 },
7312       { -1, 0 },
7313       { +1, 0 },
7314     };
7315     static int test_dir[7] =
7316     {
7317       MV_UP,
7318       MV_LEFT,
7319       MV_RIGHT,
7320       MV_DOWN,
7321       MV_UP,
7322       MV_LEFT,
7323       MV_RIGHT,
7324     };
7325     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7326     int move_preference = -1000000;     // start with very low preference
7327     int new_move_dir = MV_NONE;
7328     int start_test = RND(4);
7329     int i;
7330
7331     for (i = 0; i < NUM_DIRECTIONS; i++)
7332     {
7333       int move_dir = test_dir[start_test + i];
7334       int move_dir_preference;
7335
7336       xx = x + test_xy[start_test + i][0];
7337       yy = y + test_xy[start_test + i][1];
7338
7339       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7340           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7341       {
7342         new_move_dir = move_dir;
7343
7344         break;
7345       }
7346
7347       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7348         continue;
7349
7350       move_dir_preference = -1 * RunnerVisit[xx][yy];
7351       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7352         move_dir_preference = PlayerVisit[xx][yy];
7353
7354       if (move_dir_preference > move_preference)
7355       {
7356         // prefer field that has not been visited for the longest time
7357         move_preference = move_dir_preference;
7358         new_move_dir = move_dir;
7359       }
7360       else if (move_dir_preference == move_preference &&
7361                move_dir == old_move_dir)
7362       {
7363         // prefer last direction when all directions are preferred equally
7364         move_preference = move_dir_preference;
7365         new_move_dir = move_dir;
7366       }
7367     }
7368
7369     MovDir[x][y] = new_move_dir;
7370     if (old_move_dir != new_move_dir)
7371       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7372   }
7373 }
7374
7375 static void TurnRound(int x, int y)
7376 {
7377   int direction = MovDir[x][y];
7378
7379   TurnRoundExt(x, y);
7380
7381   GfxDir[x][y] = MovDir[x][y];
7382
7383   if (direction != MovDir[x][y])
7384     GfxFrame[x][y] = 0;
7385
7386   if (MovDelay[x][y])
7387     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7388
7389   ResetGfxFrame(x, y);
7390 }
7391
7392 static boolean JustBeingPushed(int x, int y)
7393 {
7394   int i;
7395
7396   for (i = 0; i < MAX_PLAYERS; i++)
7397   {
7398     struct PlayerInfo *player = &stored_player[i];
7399
7400     if (player->active && player->is_pushing && player->MovPos)
7401     {
7402       int next_jx = player->jx + (player->jx - player->last_jx);
7403       int next_jy = player->jy + (player->jy - player->last_jy);
7404
7405       if (x == next_jx && y == next_jy)
7406         return TRUE;
7407     }
7408   }
7409
7410   return FALSE;
7411 }
7412
7413 static void StartMoving(int x, int y)
7414 {
7415   boolean started_moving = FALSE;       // some elements can fall _and_ move
7416   int element = Feld[x][y];
7417
7418   if (Stop[x][y])
7419     return;
7420
7421   if (MovDelay[x][y] == 0)
7422     GfxAction[x][y] = ACTION_DEFAULT;
7423
7424   if (CAN_FALL(element) && y < lev_fieldy - 1)
7425   {
7426     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7427         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7428       if (JustBeingPushed(x, y))
7429         return;
7430
7431     if (element == EL_QUICKSAND_FULL)
7432     {
7433       if (IS_FREE(x, y + 1))
7434       {
7435         InitMovingField(x, y, MV_DOWN);
7436         started_moving = TRUE;
7437
7438         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7439 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7440         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7441           Store[x][y] = EL_ROCK;
7442 #else
7443         Store[x][y] = EL_ROCK;
7444 #endif
7445
7446         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7447       }
7448       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7449       {
7450         if (!MovDelay[x][y])
7451         {
7452           MovDelay[x][y] = TILEY + 1;
7453
7454           ResetGfxAnimation(x, y);
7455           ResetGfxAnimation(x, y + 1);
7456         }
7457
7458         if (MovDelay[x][y])
7459         {
7460           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7461           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7462
7463           MovDelay[x][y]--;
7464           if (MovDelay[x][y])
7465             return;
7466         }
7467
7468         Feld[x][y] = EL_QUICKSAND_EMPTY;
7469         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7470         Store[x][y + 1] = Store[x][y];
7471         Store[x][y] = 0;
7472
7473         PlayLevelSoundAction(x, y, ACTION_FILLING);
7474       }
7475       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7476       {
7477         if (!MovDelay[x][y])
7478         {
7479           MovDelay[x][y] = TILEY + 1;
7480
7481           ResetGfxAnimation(x, y);
7482           ResetGfxAnimation(x, y + 1);
7483         }
7484
7485         if (MovDelay[x][y])
7486         {
7487           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7488           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7489
7490           MovDelay[x][y]--;
7491           if (MovDelay[x][y])
7492             return;
7493         }
7494
7495         Feld[x][y] = EL_QUICKSAND_EMPTY;
7496         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7497         Store[x][y + 1] = Store[x][y];
7498         Store[x][y] = 0;
7499
7500         PlayLevelSoundAction(x, y, ACTION_FILLING);
7501       }
7502     }
7503     else if (element == EL_QUICKSAND_FAST_FULL)
7504     {
7505       if (IS_FREE(x, y + 1))
7506       {
7507         InitMovingField(x, y, MV_DOWN);
7508         started_moving = TRUE;
7509
7510         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7511 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7512         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7513           Store[x][y] = EL_ROCK;
7514 #else
7515         Store[x][y] = EL_ROCK;
7516 #endif
7517
7518         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7519       }
7520       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7521       {
7522         if (!MovDelay[x][y])
7523         {
7524           MovDelay[x][y] = TILEY + 1;
7525
7526           ResetGfxAnimation(x, y);
7527           ResetGfxAnimation(x, y + 1);
7528         }
7529
7530         if (MovDelay[x][y])
7531         {
7532           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7533           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7534
7535           MovDelay[x][y]--;
7536           if (MovDelay[x][y])
7537             return;
7538         }
7539
7540         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7541         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7542         Store[x][y + 1] = Store[x][y];
7543         Store[x][y] = 0;
7544
7545         PlayLevelSoundAction(x, y, ACTION_FILLING);
7546       }
7547       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7548       {
7549         if (!MovDelay[x][y])
7550         {
7551           MovDelay[x][y] = TILEY + 1;
7552
7553           ResetGfxAnimation(x, y);
7554           ResetGfxAnimation(x, y + 1);
7555         }
7556
7557         if (MovDelay[x][y])
7558         {
7559           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7560           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7561
7562           MovDelay[x][y]--;
7563           if (MovDelay[x][y])
7564             return;
7565         }
7566
7567         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7568         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7569         Store[x][y + 1] = Store[x][y];
7570         Store[x][y] = 0;
7571
7572         PlayLevelSoundAction(x, y, ACTION_FILLING);
7573       }
7574     }
7575     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7576              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7577     {
7578       InitMovingField(x, y, MV_DOWN);
7579       started_moving = TRUE;
7580
7581       Feld[x][y] = EL_QUICKSAND_FILLING;
7582       Store[x][y] = element;
7583
7584       PlayLevelSoundAction(x, y, ACTION_FILLING);
7585     }
7586     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7587              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7588     {
7589       InitMovingField(x, y, MV_DOWN);
7590       started_moving = TRUE;
7591
7592       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7593       Store[x][y] = element;
7594
7595       PlayLevelSoundAction(x, y, ACTION_FILLING);
7596     }
7597     else if (element == EL_MAGIC_WALL_FULL)
7598     {
7599       if (IS_FREE(x, y + 1))
7600       {
7601         InitMovingField(x, y, MV_DOWN);
7602         started_moving = TRUE;
7603
7604         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7605         Store[x][y] = EL_CHANGED(Store[x][y]);
7606       }
7607       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7608       {
7609         if (!MovDelay[x][y])
7610           MovDelay[x][y] = TILEY / 4 + 1;
7611
7612         if (MovDelay[x][y])
7613         {
7614           MovDelay[x][y]--;
7615           if (MovDelay[x][y])
7616             return;
7617         }
7618
7619         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7620         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7621         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7622         Store[x][y] = 0;
7623       }
7624     }
7625     else if (element == EL_BD_MAGIC_WALL_FULL)
7626     {
7627       if (IS_FREE(x, y + 1))
7628       {
7629         InitMovingField(x, y, MV_DOWN);
7630         started_moving = TRUE;
7631
7632         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7633         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7634       }
7635       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7636       {
7637         if (!MovDelay[x][y])
7638           MovDelay[x][y] = TILEY / 4 + 1;
7639
7640         if (MovDelay[x][y])
7641         {
7642           MovDelay[x][y]--;
7643           if (MovDelay[x][y])
7644             return;
7645         }
7646
7647         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7648         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7649         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7650         Store[x][y] = 0;
7651       }
7652     }
7653     else if (element == EL_DC_MAGIC_WALL_FULL)
7654     {
7655       if (IS_FREE(x, y + 1))
7656       {
7657         InitMovingField(x, y, MV_DOWN);
7658         started_moving = TRUE;
7659
7660         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7661         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7662       }
7663       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7664       {
7665         if (!MovDelay[x][y])
7666           MovDelay[x][y] = TILEY / 4 + 1;
7667
7668         if (MovDelay[x][y])
7669         {
7670           MovDelay[x][y]--;
7671           if (MovDelay[x][y])
7672             return;
7673         }
7674
7675         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7676         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7677         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7678         Store[x][y] = 0;
7679       }
7680     }
7681     else if ((CAN_PASS_MAGIC_WALL(element) &&
7682               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7683                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7684              (CAN_PASS_DC_MAGIC_WALL(element) &&
7685               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7686
7687     {
7688       InitMovingField(x, y, MV_DOWN);
7689       started_moving = TRUE;
7690
7691       Feld[x][y] =
7692         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7693          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7694          EL_DC_MAGIC_WALL_FILLING);
7695       Store[x][y] = element;
7696     }
7697     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7698     {
7699       SplashAcid(x, y + 1);
7700
7701       InitMovingField(x, y, MV_DOWN);
7702       started_moving = TRUE;
7703
7704       Store[x][y] = EL_ACID;
7705     }
7706     else if (
7707              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7708               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7709              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7710               CAN_FALL(element) && WasJustFalling[x][y] &&
7711               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7712
7713              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7714               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7715               (Feld[x][y + 1] == EL_BLOCKED)))
7716     {
7717       /* this is needed for a special case not covered by calling "Impact()"
7718          from "ContinueMoving()": if an element moves to a tile directly below
7719          another element which was just falling on that tile (which was empty
7720          in the previous frame), the falling element above would just stop
7721          instead of smashing the element below (in previous version, the above
7722          element was just checked for "moving" instead of "falling", resulting
7723          in incorrect smashes caused by horizontal movement of the above
7724          element; also, the case of the player being the element to smash was
7725          simply not covered here... :-/ ) */
7726
7727       CheckCollision[x][y] = 0;
7728       CheckImpact[x][y] = 0;
7729
7730       Impact(x, y);
7731     }
7732     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7733     {
7734       if (MovDir[x][y] == MV_NONE)
7735       {
7736         InitMovingField(x, y, MV_DOWN);
7737         started_moving = TRUE;
7738       }
7739     }
7740     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7741     {
7742       if (WasJustFalling[x][y]) // prevent animation from being restarted
7743         MovDir[x][y] = MV_DOWN;
7744
7745       InitMovingField(x, y, MV_DOWN);
7746       started_moving = TRUE;
7747     }
7748     else if (element == EL_AMOEBA_DROP)
7749     {
7750       Feld[x][y] = EL_AMOEBA_GROWING;
7751       Store[x][y] = EL_AMOEBA_WET;
7752     }
7753     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7754               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7755              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7756              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7757     {
7758       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7759                                 (IS_FREE(x - 1, y + 1) ||
7760                                  Feld[x - 1][y + 1] == EL_ACID));
7761       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7762                                 (IS_FREE(x + 1, y + 1) ||
7763                                  Feld[x + 1][y + 1] == EL_ACID));
7764       boolean can_fall_any  = (can_fall_left || can_fall_right);
7765       boolean can_fall_both = (can_fall_left && can_fall_right);
7766       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7767
7768       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7769       {
7770         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7771           can_fall_right = FALSE;
7772         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7773           can_fall_left = FALSE;
7774         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7775           can_fall_right = FALSE;
7776         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7777           can_fall_left = FALSE;
7778
7779         can_fall_any  = (can_fall_left || can_fall_right);
7780         can_fall_both = FALSE;
7781       }
7782
7783       if (can_fall_both)
7784       {
7785         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7786           can_fall_right = FALSE;       // slip down on left side
7787         else
7788           can_fall_left = !(can_fall_right = RND(2));
7789
7790         can_fall_both = FALSE;
7791       }
7792
7793       if (can_fall_any)
7794       {
7795         // if not determined otherwise, prefer left side for slipping down
7796         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7797         started_moving = TRUE;
7798       }
7799     }
7800     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7801     {
7802       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7803       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7804       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7805       int belt_dir = game.belt_dir[belt_nr];
7806
7807       if ((belt_dir == MV_LEFT  && left_is_free) ||
7808           (belt_dir == MV_RIGHT && right_is_free))
7809       {
7810         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7811
7812         InitMovingField(x, y, belt_dir);
7813         started_moving = TRUE;
7814
7815         Pushed[x][y] = TRUE;
7816         Pushed[nextx][y] = TRUE;
7817
7818         GfxAction[x][y] = ACTION_DEFAULT;
7819       }
7820       else
7821       {
7822         MovDir[x][y] = 0;       // if element was moving, stop it
7823       }
7824     }
7825   }
7826
7827   // not "else if" because of elements that can fall and move (EL_SPRING)
7828   if (CAN_MOVE(element) && !started_moving)
7829   {
7830     int move_pattern = element_info[element].move_pattern;
7831     int newx, newy;
7832
7833     Moving2Blocked(x, y, &newx, &newy);
7834
7835     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7836       return;
7837
7838     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7839         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7840     {
7841       WasJustMoving[x][y] = 0;
7842       CheckCollision[x][y] = 0;
7843
7844       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7845
7846       if (Feld[x][y] != element)        // element has changed
7847         return;
7848     }
7849
7850     if (!MovDelay[x][y])        // start new movement phase
7851     {
7852       // all objects that can change their move direction after each step
7853       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7854
7855       if (element != EL_YAMYAM &&
7856           element != EL_DARK_YAMYAM &&
7857           element != EL_PACMAN &&
7858           !(move_pattern & MV_ANY_DIRECTION) &&
7859           move_pattern != MV_TURNING_LEFT &&
7860           move_pattern != MV_TURNING_RIGHT &&
7861           move_pattern != MV_TURNING_LEFT_RIGHT &&
7862           move_pattern != MV_TURNING_RIGHT_LEFT &&
7863           move_pattern != MV_TURNING_RANDOM)
7864       {
7865         TurnRound(x, y);
7866
7867         if (MovDelay[x][y] && (element == EL_BUG ||
7868                                element == EL_SPACESHIP ||
7869                                element == EL_SP_SNIKSNAK ||
7870                                element == EL_SP_ELECTRON ||
7871                                element == EL_MOLE))
7872           TEST_DrawLevelField(x, y);
7873       }
7874     }
7875
7876     if (MovDelay[x][y])         // wait some time before next movement
7877     {
7878       MovDelay[x][y]--;
7879
7880       if (element == EL_ROBOT ||
7881           element == EL_YAMYAM ||
7882           element == EL_DARK_YAMYAM)
7883       {
7884         DrawLevelElementAnimationIfNeeded(x, y, element);
7885         PlayLevelSoundAction(x, y, ACTION_WAITING);
7886       }
7887       else if (element == EL_SP_ELECTRON)
7888         DrawLevelElementAnimationIfNeeded(x, y, element);
7889       else if (element == EL_DRAGON)
7890       {
7891         int i;
7892         int dir = MovDir[x][y];
7893         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7894         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7895         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7896                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7897                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7898                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7899         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7900
7901         GfxAction[x][y] = ACTION_ATTACKING;
7902
7903         if (IS_PLAYER(x, y))
7904           DrawPlayerField(x, y);
7905         else
7906           TEST_DrawLevelField(x, y);
7907
7908         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7909
7910         for (i = 1; i <= 3; i++)
7911         {
7912           int xx = x + i * dx;
7913           int yy = y + i * dy;
7914           int sx = SCREENX(xx);
7915           int sy = SCREENY(yy);
7916           int flame_graphic = graphic + (i - 1);
7917
7918           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7919             break;
7920
7921           if (MovDelay[x][y])
7922           {
7923             int flamed = MovingOrBlocked2Element(xx, yy);
7924
7925             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7926               Bang(xx, yy);
7927             else
7928               RemoveMovingField(xx, yy);
7929
7930             ChangeDelay[xx][yy] = 0;
7931
7932             Feld[xx][yy] = EL_FLAMES;
7933
7934             if (IN_SCR_FIELD(sx, sy))
7935             {
7936               TEST_DrawLevelFieldCrumbled(xx, yy);
7937               DrawGraphic(sx, sy, flame_graphic, frame);
7938             }
7939           }
7940           else
7941           {
7942             if (Feld[xx][yy] == EL_FLAMES)
7943               Feld[xx][yy] = EL_EMPTY;
7944             TEST_DrawLevelField(xx, yy);
7945           }
7946         }
7947       }
7948
7949       if (MovDelay[x][y])       // element still has to wait some time
7950       {
7951         PlayLevelSoundAction(x, y, ACTION_WAITING);
7952
7953         return;
7954       }
7955     }
7956
7957     // now make next step
7958
7959     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7960
7961     if (DONT_COLLIDE_WITH(element) &&
7962         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7963         !PLAYER_ENEMY_PROTECTED(newx, newy))
7964     {
7965       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7966
7967       return;
7968     }
7969
7970     else if (CAN_MOVE_INTO_ACID(element) &&
7971              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7972              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7973              (MovDir[x][y] == MV_DOWN ||
7974               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7975     {
7976       SplashAcid(newx, newy);
7977       Store[x][y] = EL_ACID;
7978     }
7979     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7980     {
7981       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7982           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7983           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7984           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7985       {
7986         RemoveField(x, y);
7987         TEST_DrawLevelField(x, y);
7988
7989         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7990         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7991           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7992
7993         game.friends_still_needed--;
7994         if (!game.friends_still_needed &&
7995             !game.GameOver &&
7996             game.all_players_gone)
7997           LevelSolved();
7998
7999         return;
8000       }
8001       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8002       {
8003         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8004           TEST_DrawLevelField(newx, newy);
8005         else
8006           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8007       }
8008       else if (!IS_FREE(newx, newy))
8009       {
8010         GfxAction[x][y] = ACTION_WAITING;
8011
8012         if (IS_PLAYER(x, y))
8013           DrawPlayerField(x, y);
8014         else
8015           TEST_DrawLevelField(x, y);
8016
8017         return;
8018       }
8019     }
8020     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8021     {
8022       if (IS_FOOD_PIG(Feld[newx][newy]))
8023       {
8024         if (IS_MOVING(newx, newy))
8025           RemoveMovingField(newx, newy);
8026         else
8027         {
8028           Feld[newx][newy] = EL_EMPTY;
8029           TEST_DrawLevelField(newx, newy);
8030         }
8031
8032         PlayLevelSound(x, y, SND_PIG_DIGGING);
8033       }
8034       else if (!IS_FREE(newx, newy))
8035       {
8036         if (IS_PLAYER(x, y))
8037           DrawPlayerField(x, y);
8038         else
8039           TEST_DrawLevelField(x, y);
8040
8041         return;
8042       }
8043     }
8044     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8045     {
8046       if (Store[x][y] != EL_EMPTY)
8047       {
8048         boolean can_clone = FALSE;
8049         int xx, yy;
8050
8051         // check if element to clone is still there
8052         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8053         {
8054           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8055           {
8056             can_clone = TRUE;
8057
8058             break;
8059           }
8060         }
8061
8062         // cannot clone or target field not free anymore -- do not clone
8063         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8064           Store[x][y] = EL_EMPTY;
8065       }
8066
8067       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8068       {
8069         if (IS_MV_DIAGONAL(MovDir[x][y]))
8070         {
8071           int diagonal_move_dir = MovDir[x][y];
8072           int stored = Store[x][y];
8073           int change_delay = 8;
8074           int graphic;
8075
8076           // android is moving diagonally
8077
8078           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8079
8080           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8081           GfxElement[x][y] = EL_EMC_ANDROID;
8082           GfxAction[x][y] = ACTION_SHRINKING;
8083           GfxDir[x][y] = diagonal_move_dir;
8084           ChangeDelay[x][y] = change_delay;
8085
8086           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8087                                    GfxDir[x][y]);
8088
8089           DrawLevelGraphicAnimation(x, y, graphic);
8090           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8091
8092           if (Feld[newx][newy] == EL_ACID)
8093           {
8094             SplashAcid(newx, newy);
8095
8096             return;
8097           }
8098
8099           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8100
8101           Store[newx][newy] = EL_EMC_ANDROID;
8102           GfxElement[newx][newy] = EL_EMC_ANDROID;
8103           GfxAction[newx][newy] = ACTION_GROWING;
8104           GfxDir[newx][newy] = diagonal_move_dir;
8105           ChangeDelay[newx][newy] = change_delay;
8106
8107           graphic = el_act_dir2img(GfxElement[newx][newy],
8108                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8109
8110           DrawLevelGraphicAnimation(newx, newy, graphic);
8111           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8112
8113           return;
8114         }
8115         else
8116         {
8117           Feld[newx][newy] = EL_EMPTY;
8118           TEST_DrawLevelField(newx, newy);
8119
8120           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8121         }
8122       }
8123       else if (!IS_FREE(newx, newy))
8124       {
8125         return;
8126       }
8127     }
8128     else if (IS_CUSTOM_ELEMENT(element) &&
8129              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8130     {
8131       if (!DigFieldByCE(newx, newy, element))
8132         return;
8133
8134       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8135       {
8136         RunnerVisit[x][y] = FrameCounter;
8137         PlayerVisit[x][y] /= 8;         // expire player visit path
8138       }
8139     }
8140     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8141     {
8142       if (!IS_FREE(newx, newy))
8143       {
8144         if (IS_PLAYER(x, y))
8145           DrawPlayerField(x, y);
8146         else
8147           TEST_DrawLevelField(x, y);
8148
8149         return;
8150       }
8151       else
8152       {
8153         boolean wanna_flame = !RND(10);
8154         int dx = newx - x, dy = newy - y;
8155         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8156         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8157         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8158                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8159         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8160                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8161
8162         if ((wanna_flame ||
8163              IS_CLASSIC_ENEMY(element1) ||
8164              IS_CLASSIC_ENEMY(element2)) &&
8165             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8166             element1 != EL_FLAMES && element2 != EL_FLAMES)
8167         {
8168           ResetGfxAnimation(x, y);
8169           GfxAction[x][y] = ACTION_ATTACKING;
8170
8171           if (IS_PLAYER(x, y))
8172             DrawPlayerField(x, y);
8173           else
8174             TEST_DrawLevelField(x, y);
8175
8176           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8177
8178           MovDelay[x][y] = 50;
8179
8180           Feld[newx][newy] = EL_FLAMES;
8181           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8182             Feld[newx1][newy1] = EL_FLAMES;
8183           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8184             Feld[newx2][newy2] = EL_FLAMES;
8185
8186           return;
8187         }
8188       }
8189     }
8190     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8191              Feld[newx][newy] == EL_DIAMOND)
8192     {
8193       if (IS_MOVING(newx, newy))
8194         RemoveMovingField(newx, newy);
8195       else
8196       {
8197         Feld[newx][newy] = EL_EMPTY;
8198         TEST_DrawLevelField(newx, newy);
8199       }
8200
8201       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8202     }
8203     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8204              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8205     {
8206       if (AmoebaNr[newx][newy])
8207       {
8208         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8209         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8210             Feld[newx][newy] == EL_BD_AMOEBA)
8211           AmoebaCnt[AmoebaNr[newx][newy]]--;
8212       }
8213
8214       if (IS_MOVING(newx, newy))
8215       {
8216         RemoveMovingField(newx, newy);
8217       }
8218       else
8219       {
8220         Feld[newx][newy] = EL_EMPTY;
8221         TEST_DrawLevelField(newx, newy);
8222       }
8223
8224       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8225     }
8226     else if ((element == EL_PACMAN || element == EL_MOLE)
8227              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8228     {
8229       if (AmoebaNr[newx][newy])
8230       {
8231         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8232         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8233             Feld[newx][newy] == EL_BD_AMOEBA)
8234           AmoebaCnt[AmoebaNr[newx][newy]]--;
8235       }
8236
8237       if (element == EL_MOLE)
8238       {
8239         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8240         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8241
8242         ResetGfxAnimation(x, y);
8243         GfxAction[x][y] = ACTION_DIGGING;
8244         TEST_DrawLevelField(x, y);
8245
8246         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8247
8248         return;                         // wait for shrinking amoeba
8249       }
8250       else      // element == EL_PACMAN
8251       {
8252         Feld[newx][newy] = EL_EMPTY;
8253         TEST_DrawLevelField(newx, newy);
8254         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8255       }
8256     }
8257     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8258              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8259               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8260     {
8261       // wait for shrinking amoeba to completely disappear
8262       return;
8263     }
8264     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8265     {
8266       // object was running against a wall
8267
8268       TurnRound(x, y);
8269
8270       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8271         DrawLevelElementAnimation(x, y, element);
8272
8273       if (DONT_TOUCH(element))
8274         TestIfBadThingTouchesPlayer(x, y);
8275
8276       return;
8277     }
8278
8279     InitMovingField(x, y, MovDir[x][y]);
8280
8281     PlayLevelSoundAction(x, y, ACTION_MOVING);
8282   }
8283
8284   if (MovDir[x][y])
8285     ContinueMoving(x, y);
8286 }
8287
8288 void ContinueMoving(int x, int y)
8289 {
8290   int element = Feld[x][y];
8291   struct ElementInfo *ei = &element_info[element];
8292   int direction = MovDir[x][y];
8293   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8294   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8295   int newx = x + dx, newy = y + dy;
8296   int stored = Store[x][y];
8297   int stored_new = Store[newx][newy];
8298   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8299   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8300   boolean last_line = (newy == lev_fieldy - 1);
8301
8302   MovPos[x][y] += getElementMoveStepsize(x, y);
8303
8304   if (pushed_by_player) // special case: moving object pushed by player
8305     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8306
8307   if (ABS(MovPos[x][y]) < TILEX)
8308   {
8309     TEST_DrawLevelField(x, y);
8310
8311     return;     // element is still moving
8312   }
8313
8314   // element reached destination field
8315
8316   Feld[x][y] = EL_EMPTY;
8317   Feld[newx][newy] = element;
8318   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8319
8320   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8321   {
8322     element = Feld[newx][newy] = EL_ACID;
8323   }
8324   else if (element == EL_MOLE)
8325   {
8326     Feld[x][y] = EL_SAND;
8327
8328     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8329   }
8330   else if (element == EL_QUICKSAND_FILLING)
8331   {
8332     element = Feld[newx][newy] = get_next_element(element);
8333     Store[newx][newy] = Store[x][y];
8334   }
8335   else if (element == EL_QUICKSAND_EMPTYING)
8336   {
8337     Feld[x][y] = get_next_element(element);
8338     element = Feld[newx][newy] = Store[x][y];
8339   }
8340   else if (element == EL_QUICKSAND_FAST_FILLING)
8341   {
8342     element = Feld[newx][newy] = get_next_element(element);
8343     Store[newx][newy] = Store[x][y];
8344   }
8345   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8346   {
8347     Feld[x][y] = get_next_element(element);
8348     element = Feld[newx][newy] = Store[x][y];
8349   }
8350   else if (element == EL_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_MAGIC_WALL_DEAD;
8355     Store[newx][newy] = Store[x][y];
8356   }
8357   else if (element == EL_MAGIC_WALL_EMPTYING)
8358   {
8359     Feld[x][y] = get_next_element(element);
8360     if (!game.magic_wall_active)
8361       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8362     element = Feld[newx][newy] = Store[x][y];
8363
8364     InitField(newx, newy, FALSE);
8365   }
8366   else if (element == EL_BD_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_BD_MAGIC_WALL_DEAD;
8371     Store[newx][newy] = Store[x][y];
8372   }
8373   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8374   {
8375     Feld[x][y] = get_next_element(element);
8376     if (!game.magic_wall_active)
8377       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8378     element = Feld[newx][newy] = Store[x][y];
8379
8380     InitField(newx, newy, FALSE);
8381   }
8382   else if (element == EL_DC_MAGIC_WALL_FILLING)
8383   {
8384     element = Feld[newx][newy] = get_next_element(element);
8385     if (!game.magic_wall_active)
8386       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8387     Store[newx][newy] = Store[x][y];
8388   }
8389   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8390   {
8391     Feld[x][y] = get_next_element(element);
8392     if (!game.magic_wall_active)
8393       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8394     element = Feld[newx][newy] = Store[x][y];
8395
8396     InitField(newx, newy, FALSE);
8397   }
8398   else if (element == EL_AMOEBA_DROPPING)
8399   {
8400     Feld[x][y] = get_next_element(element);
8401     element = Feld[newx][newy] = Store[x][y];
8402   }
8403   else if (element == EL_SOKOBAN_OBJECT)
8404   {
8405     if (Back[x][y])
8406       Feld[x][y] = Back[x][y];
8407
8408     if (Back[newx][newy])
8409       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8410
8411     Back[x][y] = Back[newx][newy] = 0;
8412   }
8413
8414   Store[x][y] = EL_EMPTY;
8415   MovPos[x][y] = 0;
8416   MovDir[x][y] = 0;
8417   MovDelay[x][y] = 0;
8418
8419   MovDelay[newx][newy] = 0;
8420
8421   if (CAN_CHANGE_OR_HAS_ACTION(element))
8422   {
8423     // copy element change control values to new field
8424     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8425     ChangePage[newx][newy]  = ChangePage[x][y];
8426     ChangeCount[newx][newy] = ChangeCount[x][y];
8427     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8428   }
8429
8430   CustomValue[newx][newy] = CustomValue[x][y];
8431
8432   ChangeDelay[x][y] = 0;
8433   ChangePage[x][y] = -1;
8434   ChangeCount[x][y] = 0;
8435   ChangeEvent[x][y] = -1;
8436
8437   CustomValue[x][y] = 0;
8438
8439   // copy animation control values to new field
8440   GfxFrame[newx][newy]  = GfxFrame[x][y];
8441   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8442   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8443   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8444
8445   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8446
8447   // some elements can leave other elements behind after moving
8448   if (ei->move_leave_element != EL_EMPTY &&
8449       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8450       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8451   {
8452     int move_leave_element = ei->move_leave_element;
8453
8454     // this makes it possible to leave the removed element again
8455     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8456       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8457
8458     Feld[x][y] = move_leave_element;
8459
8460     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8461       MovDir[x][y] = direction;
8462
8463     InitField(x, y, FALSE);
8464
8465     if (GFX_CRUMBLED(Feld[x][y]))
8466       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8467
8468     if (ELEM_IS_PLAYER(move_leave_element))
8469       RelocatePlayer(x, y, move_leave_element);
8470   }
8471
8472   // do this after checking for left-behind element
8473   ResetGfxAnimation(x, y);      // reset animation values for old field
8474
8475   if (!CAN_MOVE(element) ||
8476       (CAN_FALL(element) && direction == MV_DOWN &&
8477        (element == EL_SPRING ||
8478         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8479         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8480     GfxDir[x][y] = MovDir[newx][newy] = 0;
8481
8482   TEST_DrawLevelField(x, y);
8483   TEST_DrawLevelField(newx, newy);
8484
8485   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8486
8487   // prevent pushed element from moving on in pushed direction
8488   if (pushed_by_player && CAN_MOVE(element) &&
8489       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8490       !(element_info[element].move_pattern & direction))
8491     TurnRound(newx, newy);
8492
8493   // prevent elements on conveyor belt from moving on in last direction
8494   if (pushed_by_conveyor && CAN_FALL(element) &&
8495       direction & MV_HORIZONTAL)
8496     MovDir[newx][newy] = 0;
8497
8498   if (!pushed_by_player)
8499   {
8500     int nextx = newx + dx, nexty = newy + dy;
8501     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8502
8503     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8504
8505     if (CAN_FALL(element) && direction == MV_DOWN)
8506       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8507
8508     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8509       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8510
8511     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8512       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8513   }
8514
8515   if (DONT_TOUCH(element))      // object may be nasty to player or others
8516   {
8517     TestIfBadThingTouchesPlayer(newx, newy);
8518     TestIfBadThingTouchesFriend(newx, newy);
8519
8520     if (!IS_CUSTOM_ELEMENT(element))
8521       TestIfBadThingTouchesOtherBadThing(newx, newy);
8522   }
8523   else if (element == EL_PENGUIN)
8524     TestIfFriendTouchesBadThing(newx, newy);
8525
8526   if (DONT_GET_HIT_BY(element))
8527   {
8528     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8529   }
8530
8531   // give the player one last chance (one more frame) to move away
8532   if (CAN_FALL(element) && direction == MV_DOWN &&
8533       (last_line || (!IS_FREE(x, newy + 1) &&
8534                      (!IS_PLAYER(x, newy + 1) ||
8535                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8536     Impact(x, newy);
8537
8538   if (pushed_by_player && !game.use_change_when_pushing_bug)
8539   {
8540     int push_side = MV_DIR_OPPOSITE(direction);
8541     struct PlayerInfo *player = PLAYERINFO(x, y);
8542
8543     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8544                                player->index_bit, push_side);
8545     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8546                                         player->index_bit, push_side);
8547   }
8548
8549   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8550     MovDelay[newx][newy] = 1;
8551
8552   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8553
8554   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8555   TestIfElementHitsCustomElement(newx, newy, direction);
8556   TestIfPlayerTouchesCustomElement(newx, newy);
8557   TestIfElementTouchesCustomElement(newx, newy);
8558
8559   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8560       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8561     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8562                              MV_DIR_OPPOSITE(direction));
8563 }
8564
8565 int AmoebeNachbarNr(int ax, int ay)
8566 {
8567   int i;
8568   int element = Feld[ax][ay];
8569   int group_nr = 0;
8570   static int xy[4][2] =
8571   {
8572     { 0, -1 },
8573     { -1, 0 },
8574     { +1, 0 },
8575     { 0, +1 }
8576   };
8577
8578   for (i = 0; i < NUM_DIRECTIONS; i++)
8579   {
8580     int x = ax + xy[i][0];
8581     int y = ay + xy[i][1];
8582
8583     if (!IN_LEV_FIELD(x, y))
8584       continue;
8585
8586     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8587       group_nr = AmoebaNr[x][y];
8588   }
8589
8590   return group_nr;
8591 }
8592
8593 static void AmoebenVereinigen(int ax, int ay)
8594 {
8595   int i, x, y, xx, yy;
8596   int new_group_nr = AmoebaNr[ax][ay];
8597   static int xy[4][2] =
8598   {
8599     { 0, -1 },
8600     { -1, 0 },
8601     { +1, 0 },
8602     { 0, +1 }
8603   };
8604
8605   if (new_group_nr == 0)
8606     return;
8607
8608   for (i = 0; i < NUM_DIRECTIONS; i++)
8609   {
8610     x = ax + xy[i][0];
8611     y = ay + xy[i][1];
8612
8613     if (!IN_LEV_FIELD(x, y))
8614       continue;
8615
8616     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8617          Feld[x][y] == EL_BD_AMOEBA ||
8618          Feld[x][y] == EL_AMOEBA_DEAD) &&
8619         AmoebaNr[x][y] != new_group_nr)
8620     {
8621       int old_group_nr = AmoebaNr[x][y];
8622
8623       if (old_group_nr == 0)
8624         return;
8625
8626       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8627       AmoebaCnt[old_group_nr] = 0;
8628       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8629       AmoebaCnt2[old_group_nr] = 0;
8630
8631       SCAN_PLAYFIELD(xx, yy)
8632       {
8633         if (AmoebaNr[xx][yy] == old_group_nr)
8634           AmoebaNr[xx][yy] = new_group_nr;
8635       }
8636     }
8637   }
8638 }
8639
8640 void AmoebeUmwandeln(int ax, int ay)
8641 {
8642   int i, x, y;
8643
8644   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8645   {
8646     int group_nr = AmoebaNr[ax][ay];
8647
8648 #ifdef DEBUG
8649     if (group_nr == 0)
8650     {
8651       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8652       printf("AmoebeUmwandeln(): This should never happen!\n");
8653       return;
8654     }
8655 #endif
8656
8657     SCAN_PLAYFIELD(x, y)
8658     {
8659       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8660       {
8661         AmoebaNr[x][y] = 0;
8662         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8663       }
8664     }
8665
8666     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8667                             SND_AMOEBA_TURNING_TO_GEM :
8668                             SND_AMOEBA_TURNING_TO_ROCK));
8669     Bang(ax, ay);
8670   }
8671   else
8672   {
8673     static int xy[4][2] =
8674     {
8675       { 0, -1 },
8676       { -1, 0 },
8677       { +1, 0 },
8678       { 0, +1 }
8679     };
8680
8681     for (i = 0; i < NUM_DIRECTIONS; i++)
8682     {
8683       x = ax + xy[i][0];
8684       y = ay + xy[i][1];
8685
8686       if (!IN_LEV_FIELD(x, y))
8687         continue;
8688
8689       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8690       {
8691         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8692                               SND_AMOEBA_TURNING_TO_GEM :
8693                               SND_AMOEBA_TURNING_TO_ROCK));
8694         Bang(x, y);
8695       }
8696     }
8697   }
8698 }
8699
8700 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8701 {
8702   int x, y;
8703   int group_nr = AmoebaNr[ax][ay];
8704   boolean done = FALSE;
8705
8706 #ifdef DEBUG
8707   if (group_nr == 0)
8708   {
8709     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8710     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8711     return;
8712   }
8713 #endif
8714
8715   SCAN_PLAYFIELD(x, y)
8716   {
8717     if (AmoebaNr[x][y] == group_nr &&
8718         (Feld[x][y] == EL_AMOEBA_DEAD ||
8719          Feld[x][y] == EL_BD_AMOEBA ||
8720          Feld[x][y] == EL_AMOEBA_GROWING))
8721     {
8722       AmoebaNr[x][y] = 0;
8723       Feld[x][y] = new_element;
8724       InitField(x, y, FALSE);
8725       TEST_DrawLevelField(x, y);
8726       done = TRUE;
8727     }
8728   }
8729
8730   if (done)
8731     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8732                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8733                             SND_BD_AMOEBA_TURNING_TO_GEM));
8734 }
8735
8736 static void AmoebeWaechst(int x, int y)
8737 {
8738   static unsigned int sound_delay = 0;
8739   static unsigned int sound_delay_value = 0;
8740
8741   if (!MovDelay[x][y])          // start new growing cycle
8742   {
8743     MovDelay[x][y] = 7;
8744
8745     if (DelayReached(&sound_delay, sound_delay_value))
8746     {
8747       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8748       sound_delay_value = 30;
8749     }
8750   }
8751
8752   if (MovDelay[x][y])           // wait some time before growing bigger
8753   {
8754     MovDelay[x][y]--;
8755     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8756     {
8757       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8758                                            6 - MovDelay[x][y]);
8759
8760       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8761     }
8762
8763     if (!MovDelay[x][y])
8764     {
8765       Feld[x][y] = Store[x][y];
8766       Store[x][y] = 0;
8767       TEST_DrawLevelField(x, y);
8768     }
8769   }
8770 }
8771
8772 static void AmoebaDisappearing(int x, int y)
8773 {
8774   static unsigned int sound_delay = 0;
8775   static unsigned int sound_delay_value = 0;
8776
8777   if (!MovDelay[x][y])          // start new shrinking cycle
8778   {
8779     MovDelay[x][y] = 7;
8780
8781     if (DelayReached(&sound_delay, sound_delay_value))
8782       sound_delay_value = 30;
8783   }
8784
8785   if (MovDelay[x][y])           // wait some time before shrinking
8786   {
8787     MovDelay[x][y]--;
8788     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8789     {
8790       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8791                                            6 - MovDelay[x][y]);
8792
8793       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8794     }
8795
8796     if (!MovDelay[x][y])
8797     {
8798       Feld[x][y] = EL_EMPTY;
8799       TEST_DrawLevelField(x, y);
8800
8801       // don't let mole enter this field in this cycle;
8802       // (give priority to objects falling to this field from above)
8803       Stop[x][y] = TRUE;
8804     }
8805   }
8806 }
8807
8808 static void AmoebeAbleger(int ax, int ay)
8809 {
8810   int i;
8811   int element = Feld[ax][ay];
8812   int graphic = el2img(element);
8813   int newax = ax, neway = ay;
8814   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8815   static int xy[4][2] =
8816   {
8817     { 0, -1 },
8818     { -1, 0 },
8819     { +1, 0 },
8820     { 0, +1 }
8821   };
8822
8823   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8824   {
8825     Feld[ax][ay] = EL_AMOEBA_DEAD;
8826     TEST_DrawLevelField(ax, ay);
8827     return;
8828   }
8829
8830   if (IS_ANIMATED(graphic))
8831     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8832
8833   if (!MovDelay[ax][ay])        // start making new amoeba field
8834     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8835
8836   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8837   {
8838     MovDelay[ax][ay]--;
8839     if (MovDelay[ax][ay])
8840       return;
8841   }
8842
8843   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8844   {
8845     int start = RND(4);
8846     int x = ax + xy[start][0];
8847     int y = ay + xy[start][1];
8848
8849     if (!IN_LEV_FIELD(x, y))
8850       return;
8851
8852     if (IS_FREE(x, y) ||
8853         CAN_GROW_INTO(Feld[x][y]) ||
8854         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8855         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8856     {
8857       newax = x;
8858       neway = y;
8859     }
8860
8861     if (newax == ax && neway == ay)
8862       return;
8863   }
8864   else                          // normal or "filled" (BD style) amoeba
8865   {
8866     int start = RND(4);
8867     boolean waiting_for_player = FALSE;
8868
8869     for (i = 0; i < NUM_DIRECTIONS; i++)
8870     {
8871       int j = (start + i) % 4;
8872       int x = ax + xy[j][0];
8873       int y = ay + xy[j][1];
8874
8875       if (!IN_LEV_FIELD(x, y))
8876         continue;
8877
8878       if (IS_FREE(x, y) ||
8879           CAN_GROW_INTO(Feld[x][y]) ||
8880           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8881           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8882       {
8883         newax = x;
8884         neway = y;
8885         break;
8886       }
8887       else if (IS_PLAYER(x, y))
8888         waiting_for_player = TRUE;
8889     }
8890
8891     if (newax == ax && neway == ay)             // amoeba cannot grow
8892     {
8893       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8894       {
8895         Feld[ax][ay] = EL_AMOEBA_DEAD;
8896         TEST_DrawLevelField(ax, ay);
8897         AmoebaCnt[AmoebaNr[ax][ay]]--;
8898
8899         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8900         {
8901           if (element == EL_AMOEBA_FULL)
8902             AmoebeUmwandeln(ax, ay);
8903           else if (element == EL_BD_AMOEBA)
8904             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8905         }
8906       }
8907       return;
8908     }
8909     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8910     {
8911       // amoeba gets larger by growing in some direction
8912
8913       int new_group_nr = AmoebaNr[ax][ay];
8914
8915 #ifdef DEBUG
8916   if (new_group_nr == 0)
8917   {
8918     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8919     printf("AmoebeAbleger(): This should never happen!\n");
8920     return;
8921   }
8922 #endif
8923
8924       AmoebaNr[newax][neway] = new_group_nr;
8925       AmoebaCnt[new_group_nr]++;
8926       AmoebaCnt2[new_group_nr]++;
8927
8928       // if amoeba touches other amoeba(s) after growing, unify them
8929       AmoebenVereinigen(newax, neway);
8930
8931       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8932       {
8933         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8934         return;
8935       }
8936     }
8937   }
8938
8939   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8940       (neway == lev_fieldy - 1 && newax != ax))
8941   {
8942     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8943     Store[newax][neway] = element;
8944   }
8945   else if (neway == ay || element == EL_EMC_DRIPPER)
8946   {
8947     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8948
8949     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8950   }
8951   else
8952   {
8953     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8954     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8955     Store[ax][ay] = EL_AMOEBA_DROP;
8956     ContinueMoving(ax, ay);
8957     return;
8958   }
8959
8960   TEST_DrawLevelField(newax, neway);
8961 }
8962
8963 static void Life(int ax, int ay)
8964 {
8965   int x1, y1, x2, y2;
8966   int life_time = 40;
8967   int element = Feld[ax][ay];
8968   int graphic = el2img(element);
8969   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8970                          level.biomaze);
8971   boolean changed = FALSE;
8972
8973   if (IS_ANIMATED(graphic))
8974     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8975
8976   if (Stop[ax][ay])
8977     return;
8978
8979   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8980     MovDelay[ax][ay] = life_time;
8981
8982   if (MovDelay[ax][ay])         // wait some time before next cycle
8983   {
8984     MovDelay[ax][ay]--;
8985     if (MovDelay[ax][ay])
8986       return;
8987   }
8988
8989   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8990   {
8991     int xx = ax+x1, yy = ay+y1;
8992     int old_element = Feld[xx][yy];
8993     int num_neighbours = 0;
8994
8995     if (!IN_LEV_FIELD(xx, yy))
8996       continue;
8997
8998     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8999     {
9000       int x = xx+x2, y = yy+y2;
9001
9002       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9003         continue;
9004
9005       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9006       boolean is_neighbour = FALSE;
9007
9008       if (level.use_life_bugs)
9009         is_neighbour =
9010           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9011            (IS_FREE(x, y)                             &&  Stop[x][y]));
9012       else
9013         is_neighbour =
9014           (Last[x][y] == element || is_player_cell);
9015
9016       if (is_neighbour)
9017         num_neighbours++;
9018     }
9019
9020     boolean is_free = FALSE;
9021
9022     if (level.use_life_bugs)
9023       is_free = (IS_FREE(xx, yy));
9024     else
9025       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9026
9027     if (xx == ax && yy == ay)           // field in the middle
9028     {
9029       if (num_neighbours < life_parameter[0] ||
9030           num_neighbours > life_parameter[1])
9031       {
9032         Feld[xx][yy] = EL_EMPTY;
9033         if (Feld[xx][yy] != old_element)
9034           TEST_DrawLevelField(xx, yy);
9035         Stop[xx][yy] = TRUE;
9036         changed = TRUE;
9037       }
9038     }
9039     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9040     {                                   // free border field
9041       if (num_neighbours >= life_parameter[2] &&
9042           num_neighbours <= life_parameter[3])
9043       {
9044         Feld[xx][yy] = element;
9045         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9046         if (Feld[xx][yy] != old_element)
9047           TEST_DrawLevelField(xx, yy);
9048         Stop[xx][yy] = TRUE;
9049         changed = TRUE;
9050       }
9051     }
9052   }
9053
9054   if (changed)
9055     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9056                    SND_GAME_OF_LIFE_GROWING);
9057 }
9058
9059 static void InitRobotWheel(int x, int y)
9060 {
9061   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9062 }
9063
9064 static void RunRobotWheel(int x, int y)
9065 {
9066   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9067 }
9068
9069 static void StopRobotWheel(int x, int y)
9070 {
9071   if (game.robot_wheel_x == x &&
9072       game.robot_wheel_y == y)
9073   {
9074     game.robot_wheel_x = -1;
9075     game.robot_wheel_y = -1;
9076     game.robot_wheel_active = FALSE;
9077   }
9078 }
9079
9080 static void InitTimegateWheel(int x, int y)
9081 {
9082   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9083 }
9084
9085 static void RunTimegateWheel(int x, int y)
9086 {
9087   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9088 }
9089
9090 static void InitMagicBallDelay(int x, int y)
9091 {
9092   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9093 }
9094
9095 static void ActivateMagicBall(int bx, int by)
9096 {
9097   int x, y;
9098
9099   if (level.ball_random)
9100   {
9101     int pos_border = RND(8);    // select one of the eight border elements
9102     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9103     int xx = pos_content % 3;
9104     int yy = pos_content / 3;
9105
9106     x = bx - 1 + xx;
9107     y = by - 1 + yy;
9108
9109     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9110       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9111   }
9112   else
9113   {
9114     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9115     {
9116       int xx = x - bx + 1;
9117       int yy = y - by + 1;
9118
9119       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9120         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9121     }
9122   }
9123
9124   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9125 }
9126
9127 static void CheckExit(int x, int y)
9128 {
9129   if (game.gems_still_needed > 0 ||
9130       game.sokoban_fields_still_needed > 0 ||
9131       game.sokoban_objects_still_needed > 0 ||
9132       game.lights_still_needed > 0)
9133   {
9134     int element = Feld[x][y];
9135     int graphic = el2img(element);
9136
9137     if (IS_ANIMATED(graphic))
9138       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9139
9140     return;
9141   }
9142
9143   // do not re-open exit door closed after last player
9144   if (game.all_players_gone)
9145     return;
9146
9147   Feld[x][y] = EL_EXIT_OPENING;
9148
9149   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9150 }
9151
9152 static void CheckExitEM(int x, int y)
9153 {
9154   if (game.gems_still_needed > 0 ||
9155       game.sokoban_fields_still_needed > 0 ||
9156       game.sokoban_objects_still_needed > 0 ||
9157       game.lights_still_needed > 0)
9158   {
9159     int element = Feld[x][y];
9160     int graphic = el2img(element);
9161
9162     if (IS_ANIMATED(graphic))
9163       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9164
9165     return;
9166   }
9167
9168   // do not re-open exit door closed after last player
9169   if (game.all_players_gone)
9170     return;
9171
9172   Feld[x][y] = EL_EM_EXIT_OPENING;
9173
9174   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9175 }
9176
9177 static void CheckExitSteel(int x, int y)
9178 {
9179   if (game.gems_still_needed > 0 ||
9180       game.sokoban_fields_still_needed > 0 ||
9181       game.sokoban_objects_still_needed > 0 ||
9182       game.lights_still_needed > 0)
9183   {
9184     int element = Feld[x][y];
9185     int graphic = el2img(element);
9186
9187     if (IS_ANIMATED(graphic))
9188       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9189
9190     return;
9191   }
9192
9193   // do not re-open exit door closed after last player
9194   if (game.all_players_gone)
9195     return;
9196
9197   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9198
9199   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9200 }
9201
9202 static void CheckExitSteelEM(int x, int y)
9203 {
9204   if (game.gems_still_needed > 0 ||
9205       game.sokoban_fields_still_needed > 0 ||
9206       game.sokoban_objects_still_needed > 0 ||
9207       game.lights_still_needed > 0)
9208   {
9209     int element = Feld[x][y];
9210     int graphic = el2img(element);
9211
9212     if (IS_ANIMATED(graphic))
9213       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9214
9215     return;
9216   }
9217
9218   // do not re-open exit door closed after last player
9219   if (game.all_players_gone)
9220     return;
9221
9222   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9223
9224   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9225 }
9226
9227 static void CheckExitSP(int x, int y)
9228 {
9229   if (game.gems_still_needed > 0)
9230   {
9231     int element = Feld[x][y];
9232     int graphic = el2img(element);
9233
9234     if (IS_ANIMATED(graphic))
9235       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9236
9237     return;
9238   }
9239
9240   // do not re-open exit door closed after last player
9241   if (game.all_players_gone)
9242     return;
9243
9244   Feld[x][y] = EL_SP_EXIT_OPENING;
9245
9246   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9247 }
9248
9249 static void CloseAllOpenTimegates(void)
9250 {
9251   int x, y;
9252
9253   SCAN_PLAYFIELD(x, y)
9254   {
9255     int element = Feld[x][y];
9256
9257     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9258     {
9259       Feld[x][y] = EL_TIMEGATE_CLOSING;
9260
9261       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9262     }
9263   }
9264 }
9265
9266 static void DrawTwinkleOnField(int x, int y)
9267 {
9268   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9269     return;
9270
9271   if (Feld[x][y] == EL_BD_DIAMOND)
9272     return;
9273
9274   if (MovDelay[x][y] == 0)      // next animation frame
9275     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9276
9277   if (MovDelay[x][y] != 0)      // wait some time before next frame
9278   {
9279     MovDelay[x][y]--;
9280
9281     DrawLevelElementAnimation(x, y, Feld[x][y]);
9282
9283     if (MovDelay[x][y] != 0)
9284     {
9285       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9286                                            10 - MovDelay[x][y]);
9287
9288       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9289     }
9290   }
9291 }
9292
9293 static void MauerWaechst(int x, int y)
9294 {
9295   int delay = 6;
9296
9297   if (!MovDelay[x][y])          // next animation frame
9298     MovDelay[x][y] = 3 * delay;
9299
9300   if (MovDelay[x][y])           // wait some time before next frame
9301   {
9302     MovDelay[x][y]--;
9303
9304     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9305     {
9306       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9307       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9308
9309       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9310     }
9311
9312     if (!MovDelay[x][y])
9313     {
9314       if (MovDir[x][y] == MV_LEFT)
9315       {
9316         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9317           TEST_DrawLevelField(x - 1, y);
9318       }
9319       else if (MovDir[x][y] == MV_RIGHT)
9320       {
9321         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9322           TEST_DrawLevelField(x + 1, y);
9323       }
9324       else if (MovDir[x][y] == MV_UP)
9325       {
9326         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9327           TEST_DrawLevelField(x, y - 1);
9328       }
9329       else
9330       {
9331         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9332           TEST_DrawLevelField(x, y + 1);
9333       }
9334
9335       Feld[x][y] = Store[x][y];
9336       Store[x][y] = 0;
9337       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9338       TEST_DrawLevelField(x, y);
9339     }
9340   }
9341 }
9342
9343 static void MauerAbleger(int ax, int ay)
9344 {
9345   int element = Feld[ax][ay];
9346   int graphic = el2img(element);
9347   boolean oben_frei = FALSE, unten_frei = FALSE;
9348   boolean links_frei = FALSE, rechts_frei = FALSE;
9349   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9350   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9351   boolean new_wall = FALSE;
9352
9353   if (IS_ANIMATED(graphic))
9354     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9355
9356   if (!MovDelay[ax][ay])        // start building new wall
9357     MovDelay[ax][ay] = 6;
9358
9359   if (MovDelay[ax][ay])         // wait some time before building new wall
9360   {
9361     MovDelay[ax][ay]--;
9362     if (MovDelay[ax][ay])
9363       return;
9364   }
9365
9366   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9367     oben_frei = TRUE;
9368   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9369     unten_frei = TRUE;
9370   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9371     links_frei = TRUE;
9372   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9373     rechts_frei = TRUE;
9374
9375   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9376       element == EL_EXPANDABLE_WALL_ANY)
9377   {
9378     if (oben_frei)
9379     {
9380       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9381       Store[ax][ay-1] = element;
9382       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9383       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9384         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9385                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9386       new_wall = TRUE;
9387     }
9388     if (unten_frei)
9389     {
9390       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9391       Store[ax][ay+1] = element;
9392       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9393       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9394         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9395                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9396       new_wall = TRUE;
9397     }
9398   }
9399
9400   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9401       element == EL_EXPANDABLE_WALL_ANY ||
9402       element == EL_EXPANDABLE_WALL ||
9403       element == EL_BD_EXPANDABLE_WALL)
9404   {
9405     if (links_frei)
9406     {
9407       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9408       Store[ax-1][ay] = element;
9409       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9410       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9411         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9412                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9413       new_wall = TRUE;
9414     }
9415
9416     if (rechts_frei)
9417     {
9418       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9419       Store[ax+1][ay] = element;
9420       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9421       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9422         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9423                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9424       new_wall = TRUE;
9425     }
9426   }
9427
9428   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9429     TEST_DrawLevelField(ax, ay);
9430
9431   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9432     oben_massiv = TRUE;
9433   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9434     unten_massiv = TRUE;
9435   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9436     links_massiv = TRUE;
9437   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9438     rechts_massiv = TRUE;
9439
9440   if (((oben_massiv && unten_massiv) ||
9441        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9442        element == EL_EXPANDABLE_WALL) &&
9443       ((links_massiv && rechts_massiv) ||
9444        element == EL_EXPANDABLE_WALL_VERTICAL))
9445     Feld[ax][ay] = EL_WALL;
9446
9447   if (new_wall)
9448     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9449 }
9450
9451 static void MauerAblegerStahl(int ax, int ay)
9452 {
9453   int element = Feld[ax][ay];
9454   int graphic = el2img(element);
9455   boolean oben_frei = FALSE, unten_frei = FALSE;
9456   boolean links_frei = FALSE, rechts_frei = FALSE;
9457   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9458   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9459   boolean new_wall = FALSE;
9460
9461   if (IS_ANIMATED(graphic))
9462     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9463
9464   if (!MovDelay[ax][ay])        // start building new wall
9465     MovDelay[ax][ay] = 6;
9466
9467   if (MovDelay[ax][ay])         // wait some time before building new wall
9468   {
9469     MovDelay[ax][ay]--;
9470     if (MovDelay[ax][ay])
9471       return;
9472   }
9473
9474   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9475     oben_frei = TRUE;
9476   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9477     unten_frei = TRUE;
9478   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9479     links_frei = TRUE;
9480   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9481     rechts_frei = TRUE;
9482
9483   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9484       element == EL_EXPANDABLE_STEELWALL_ANY)
9485   {
9486     if (oben_frei)
9487     {
9488       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9489       Store[ax][ay-1] = element;
9490       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9491       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9492         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9493                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9494       new_wall = TRUE;
9495     }
9496     if (unten_frei)
9497     {
9498       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9499       Store[ax][ay+1] = element;
9500       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9501       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9502         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9503                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9504       new_wall = TRUE;
9505     }
9506   }
9507
9508   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9509       element == EL_EXPANDABLE_STEELWALL_ANY)
9510   {
9511     if (links_frei)
9512     {
9513       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9514       Store[ax-1][ay] = element;
9515       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9516       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9517         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9518                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9519       new_wall = TRUE;
9520     }
9521
9522     if (rechts_frei)
9523     {
9524       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9525       Store[ax+1][ay] = element;
9526       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9527       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9528         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9529                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9530       new_wall = TRUE;
9531     }
9532   }
9533
9534   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9535     oben_massiv = TRUE;
9536   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9537     unten_massiv = TRUE;
9538   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9539     links_massiv = TRUE;
9540   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9541     rechts_massiv = TRUE;
9542
9543   if (((oben_massiv && unten_massiv) ||
9544        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9545       ((links_massiv && rechts_massiv) ||
9546        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9547     Feld[ax][ay] = EL_STEELWALL;
9548
9549   if (new_wall)
9550     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9551 }
9552
9553 static void CheckForDragon(int x, int y)
9554 {
9555   int i, j;
9556   boolean dragon_found = FALSE;
9557   static int xy[4][2] =
9558   {
9559     { 0, -1 },
9560     { -1, 0 },
9561     { +1, 0 },
9562     { 0, +1 }
9563   };
9564
9565   for (i = 0; i < NUM_DIRECTIONS; i++)
9566   {
9567     for (j = 0; j < 4; j++)
9568     {
9569       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9570
9571       if (IN_LEV_FIELD(xx, yy) &&
9572           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9573       {
9574         if (Feld[xx][yy] == EL_DRAGON)
9575           dragon_found = TRUE;
9576       }
9577       else
9578         break;
9579     }
9580   }
9581
9582   if (!dragon_found)
9583   {
9584     for (i = 0; i < NUM_DIRECTIONS; i++)
9585     {
9586       for (j = 0; j < 3; j++)
9587       {
9588         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9589   
9590         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9591         {
9592           Feld[xx][yy] = EL_EMPTY;
9593           TEST_DrawLevelField(xx, yy);
9594         }
9595         else
9596           break;
9597       }
9598     }
9599   }
9600 }
9601
9602 static void InitBuggyBase(int x, int y)
9603 {
9604   int element = Feld[x][y];
9605   int activating_delay = FRAMES_PER_SECOND / 4;
9606
9607   ChangeDelay[x][y] =
9608     (element == EL_SP_BUGGY_BASE ?
9609      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9610      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9611      activating_delay :
9612      element == EL_SP_BUGGY_BASE_ACTIVE ?
9613      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9614 }
9615
9616 static void WarnBuggyBase(int x, int y)
9617 {
9618   int i;
9619   static int xy[4][2] =
9620   {
9621     { 0, -1 },
9622     { -1, 0 },
9623     { +1, 0 },
9624     { 0, +1 }
9625   };
9626
9627   for (i = 0; i < NUM_DIRECTIONS; i++)
9628   {
9629     int xx = x + xy[i][0];
9630     int yy = y + xy[i][1];
9631
9632     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9633     {
9634       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9635
9636       break;
9637     }
9638   }
9639 }
9640
9641 static void InitTrap(int x, int y)
9642 {
9643   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9644 }
9645
9646 static void ActivateTrap(int x, int y)
9647 {
9648   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9649 }
9650
9651 static void ChangeActiveTrap(int x, int y)
9652 {
9653   int graphic = IMG_TRAP_ACTIVE;
9654
9655   // if new animation frame was drawn, correct crumbled sand border
9656   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9657     TEST_DrawLevelFieldCrumbled(x, y);
9658 }
9659
9660 static int getSpecialActionElement(int element, int number, int base_element)
9661 {
9662   return (element != EL_EMPTY ? element :
9663           number != -1 ? base_element + number - 1 :
9664           EL_EMPTY);
9665 }
9666
9667 static int getModifiedActionNumber(int value_old, int operator, int operand,
9668                                    int value_min, int value_max)
9669 {
9670   int value_new = (operator == CA_MODE_SET      ? operand :
9671                    operator == CA_MODE_ADD      ? value_old + operand :
9672                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9673                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9674                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9675                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9676                    value_old);
9677
9678   return (value_new < value_min ? value_min :
9679           value_new > value_max ? value_max :
9680           value_new);
9681 }
9682
9683 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9684 {
9685   struct ElementInfo *ei = &element_info[element];
9686   struct ElementChangeInfo *change = &ei->change_page[page];
9687   int target_element = change->target_element;
9688   int action_type = change->action_type;
9689   int action_mode = change->action_mode;
9690   int action_arg = change->action_arg;
9691   int action_element = change->action_element;
9692   int i;
9693
9694   if (!change->has_action)
9695     return;
9696
9697   // ---------- determine action paramater values -----------------------------
9698
9699   int level_time_value =
9700     (level.time > 0 ? TimeLeft :
9701      TimePlayed);
9702
9703   int action_arg_element_raw =
9704     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9705      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9706      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9707      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9708      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9709      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9710      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9711      EL_EMPTY);
9712   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9713
9714   int action_arg_direction =
9715     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9716      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9717      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9718      change->actual_trigger_side :
9719      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9720      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9721      MV_NONE);
9722
9723   int action_arg_number_min =
9724     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9725      CA_ARG_MIN);
9726
9727   int action_arg_number_max =
9728     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9729      action_type == CA_SET_LEVEL_GEMS ? 999 :
9730      action_type == CA_SET_LEVEL_TIME ? 9999 :
9731      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9732      action_type == CA_SET_CE_VALUE ? 9999 :
9733      action_type == CA_SET_CE_SCORE ? 9999 :
9734      CA_ARG_MAX);
9735
9736   int action_arg_number_reset =
9737     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9738      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9739      action_type == CA_SET_LEVEL_TIME ? level.time :
9740      action_type == CA_SET_LEVEL_SCORE ? 0 :
9741      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9742      action_type == CA_SET_CE_SCORE ? 0 :
9743      0);
9744
9745   int action_arg_number =
9746     (action_arg <= CA_ARG_MAX ? action_arg :
9747      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9748      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9749      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9750      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9751      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9752      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9753      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9754      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9755      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9756      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9757      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9758      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9759      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9760      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9761      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9762      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9763      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9764      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9765      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9766      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9767      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9768      -1);
9769
9770   int action_arg_number_old =
9771     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9772      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9773      action_type == CA_SET_LEVEL_SCORE ? game.score :
9774      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9775      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9776      0);
9777
9778   int action_arg_number_new =
9779     getModifiedActionNumber(action_arg_number_old,
9780                             action_mode, action_arg_number,
9781                             action_arg_number_min, action_arg_number_max);
9782
9783   int trigger_player_bits =
9784     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9785      change->actual_trigger_player_bits : change->trigger_player);
9786
9787   int action_arg_player_bits =
9788     (action_arg >= CA_ARG_PLAYER_1 &&
9789      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9790      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9791      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9792      PLAYER_BITS_ANY);
9793
9794   // ---------- execute action  -----------------------------------------------
9795
9796   switch (action_type)
9797   {
9798     case CA_NO_ACTION:
9799     {
9800       return;
9801     }
9802
9803     // ---------- level actions  ----------------------------------------------
9804
9805     case CA_RESTART_LEVEL:
9806     {
9807       game.restart_level = TRUE;
9808
9809       break;
9810     }
9811
9812     case CA_SHOW_ENVELOPE:
9813     {
9814       int element = getSpecialActionElement(action_arg_element,
9815                                             action_arg_number, EL_ENVELOPE_1);
9816
9817       if (IS_ENVELOPE(element))
9818         local_player->show_envelope = element;
9819
9820       break;
9821     }
9822
9823     case CA_SET_LEVEL_TIME:
9824     {
9825       if (level.time > 0)       // only modify limited time value
9826       {
9827         TimeLeft = action_arg_number_new;
9828
9829         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9830
9831         DisplayGameControlValues();
9832
9833         if (!TimeLeft && setup.time_limit)
9834           for (i = 0; i < MAX_PLAYERS; i++)
9835             KillPlayer(&stored_player[i]);
9836       }
9837
9838       break;
9839     }
9840
9841     case CA_SET_LEVEL_SCORE:
9842     {
9843       game.score = action_arg_number_new;
9844
9845       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9846
9847       DisplayGameControlValues();
9848
9849       break;
9850     }
9851
9852     case CA_SET_LEVEL_GEMS:
9853     {
9854       game.gems_still_needed = action_arg_number_new;
9855
9856       game.snapshot.collected_item = TRUE;
9857
9858       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9859
9860       DisplayGameControlValues();
9861
9862       break;
9863     }
9864
9865     case CA_SET_LEVEL_WIND:
9866     {
9867       game.wind_direction = action_arg_direction;
9868
9869       break;
9870     }
9871
9872     case CA_SET_LEVEL_RANDOM_SEED:
9873     {
9874       // ensure that setting a new random seed while playing is predictable
9875       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9876
9877       break;
9878     }
9879
9880     // ---------- player actions  ---------------------------------------------
9881
9882     case CA_MOVE_PLAYER:
9883     case CA_MOVE_PLAYER_NEW:
9884     {
9885       // automatically move to the next field in specified direction
9886       for (i = 0; i < MAX_PLAYERS; i++)
9887         if (trigger_player_bits & (1 << i))
9888           if (action_type == CA_MOVE_PLAYER ||
9889               stored_player[i].MovPos == 0)
9890             stored_player[i].programmed_action = action_arg_direction;
9891
9892       break;
9893     }
9894
9895     case CA_EXIT_PLAYER:
9896     {
9897       for (i = 0; i < MAX_PLAYERS; i++)
9898         if (action_arg_player_bits & (1 << i))
9899           ExitPlayer(&stored_player[i]);
9900
9901       if (game.players_still_needed == 0)
9902         LevelSolved();
9903
9904       break;
9905     }
9906
9907     case CA_KILL_PLAYER:
9908     {
9909       for (i = 0; i < MAX_PLAYERS; i++)
9910         if (action_arg_player_bits & (1 << i))
9911           KillPlayer(&stored_player[i]);
9912
9913       break;
9914     }
9915
9916     case CA_SET_PLAYER_KEYS:
9917     {
9918       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9919       int element = getSpecialActionElement(action_arg_element,
9920                                             action_arg_number, EL_KEY_1);
9921
9922       if (IS_KEY(element))
9923       {
9924         for (i = 0; i < MAX_PLAYERS; i++)
9925         {
9926           if (trigger_player_bits & (1 << i))
9927           {
9928             stored_player[i].key[KEY_NR(element)] = key_state;
9929
9930             DrawGameDoorValues();
9931           }
9932         }
9933       }
9934
9935       break;
9936     }
9937
9938     case CA_SET_PLAYER_SPEED:
9939     {
9940       for (i = 0; i < MAX_PLAYERS; i++)
9941       {
9942         if (trigger_player_bits & (1 << i))
9943         {
9944           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9945
9946           if (action_arg == CA_ARG_SPEED_FASTER &&
9947               stored_player[i].cannot_move)
9948           {
9949             action_arg_number = STEPSIZE_VERY_SLOW;
9950           }
9951           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9952                    action_arg == CA_ARG_SPEED_FASTER)
9953           {
9954             action_arg_number = 2;
9955             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9956                            CA_MODE_MULTIPLY);
9957           }
9958           else if (action_arg == CA_ARG_NUMBER_RESET)
9959           {
9960             action_arg_number = level.initial_player_stepsize[i];
9961           }
9962
9963           move_stepsize =
9964             getModifiedActionNumber(move_stepsize,
9965                                     action_mode,
9966                                     action_arg_number,
9967                                     action_arg_number_min,
9968                                     action_arg_number_max);
9969
9970           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9971         }
9972       }
9973
9974       break;
9975     }
9976
9977     case CA_SET_PLAYER_SHIELD:
9978     {
9979       for (i = 0; i < MAX_PLAYERS; i++)
9980       {
9981         if (trigger_player_bits & (1 << i))
9982         {
9983           if (action_arg == CA_ARG_SHIELD_OFF)
9984           {
9985             stored_player[i].shield_normal_time_left = 0;
9986             stored_player[i].shield_deadly_time_left = 0;
9987           }
9988           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9989           {
9990             stored_player[i].shield_normal_time_left = 999999;
9991           }
9992           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9993           {
9994             stored_player[i].shield_normal_time_left = 999999;
9995             stored_player[i].shield_deadly_time_left = 999999;
9996           }
9997         }
9998       }
9999
10000       break;
10001     }
10002
10003     case CA_SET_PLAYER_GRAVITY:
10004     {
10005       for (i = 0; i < MAX_PLAYERS; i++)
10006       {
10007         if (trigger_player_bits & (1 << i))
10008         {
10009           stored_player[i].gravity =
10010             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10011              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10012              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10013              stored_player[i].gravity);
10014         }
10015       }
10016
10017       break;
10018     }
10019
10020     case CA_SET_PLAYER_ARTWORK:
10021     {
10022       for (i = 0; i < MAX_PLAYERS; i++)
10023       {
10024         if (trigger_player_bits & (1 << i))
10025         {
10026           int artwork_element = action_arg_element;
10027
10028           if (action_arg == CA_ARG_ELEMENT_RESET)
10029             artwork_element =
10030               (level.use_artwork_element[i] ? level.artwork_element[i] :
10031                stored_player[i].element_nr);
10032
10033           if (stored_player[i].artwork_element != artwork_element)
10034             stored_player[i].Frame = 0;
10035
10036           stored_player[i].artwork_element = artwork_element;
10037
10038           SetPlayerWaiting(&stored_player[i], FALSE);
10039
10040           // set number of special actions for bored and sleeping animation
10041           stored_player[i].num_special_action_bored =
10042             get_num_special_action(artwork_element,
10043                                    ACTION_BORING_1, ACTION_BORING_LAST);
10044           stored_player[i].num_special_action_sleeping =
10045             get_num_special_action(artwork_element,
10046                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10047         }
10048       }
10049
10050       break;
10051     }
10052
10053     case CA_SET_PLAYER_INVENTORY:
10054     {
10055       for (i = 0; i < MAX_PLAYERS; i++)
10056       {
10057         struct PlayerInfo *player = &stored_player[i];
10058         int j, k;
10059
10060         if (trigger_player_bits & (1 << i))
10061         {
10062           int inventory_element = action_arg_element;
10063
10064           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10065               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10066               action_arg == CA_ARG_ELEMENT_ACTION)
10067           {
10068             int element = inventory_element;
10069             int collect_count = element_info[element].collect_count_initial;
10070
10071             if (!IS_CUSTOM_ELEMENT(element))
10072               collect_count = 1;
10073
10074             if (collect_count == 0)
10075               player->inventory_infinite_element = element;
10076             else
10077               for (k = 0; k < collect_count; k++)
10078                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10079                   player->inventory_element[player->inventory_size++] =
10080                     element;
10081           }
10082           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10083                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10084                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10085           {
10086             if (player->inventory_infinite_element != EL_UNDEFINED &&
10087                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10088                                      action_arg_element_raw))
10089               player->inventory_infinite_element = EL_UNDEFINED;
10090
10091             for (k = 0, j = 0; j < player->inventory_size; j++)
10092             {
10093               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10094                                         action_arg_element_raw))
10095                 player->inventory_element[k++] = player->inventory_element[j];
10096             }
10097
10098             player->inventory_size = k;
10099           }
10100           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10101           {
10102             if (player->inventory_size > 0)
10103             {
10104               for (j = 0; j < player->inventory_size - 1; j++)
10105                 player->inventory_element[j] = player->inventory_element[j + 1];
10106
10107               player->inventory_size--;
10108             }
10109           }
10110           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10111           {
10112             if (player->inventory_size > 0)
10113               player->inventory_size--;
10114           }
10115           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10116           {
10117             player->inventory_infinite_element = EL_UNDEFINED;
10118             player->inventory_size = 0;
10119           }
10120           else if (action_arg == CA_ARG_INVENTORY_RESET)
10121           {
10122             player->inventory_infinite_element = EL_UNDEFINED;
10123             player->inventory_size = 0;
10124
10125             if (level.use_initial_inventory[i])
10126             {
10127               for (j = 0; j < level.initial_inventory_size[i]; j++)
10128               {
10129                 int element = level.initial_inventory_content[i][j];
10130                 int collect_count = element_info[element].collect_count_initial;
10131
10132                 if (!IS_CUSTOM_ELEMENT(element))
10133                   collect_count = 1;
10134
10135                 if (collect_count == 0)
10136                   player->inventory_infinite_element = element;
10137                 else
10138                   for (k = 0; k < collect_count; k++)
10139                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10140                       player->inventory_element[player->inventory_size++] =
10141                         element;
10142               }
10143             }
10144           }
10145         }
10146       }
10147
10148       break;
10149     }
10150
10151     // ---------- CE actions  -------------------------------------------------
10152
10153     case CA_SET_CE_VALUE:
10154     {
10155       int last_ce_value = CustomValue[x][y];
10156
10157       CustomValue[x][y] = action_arg_number_new;
10158
10159       if (CustomValue[x][y] != last_ce_value)
10160       {
10161         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10162         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10163
10164         if (CustomValue[x][y] == 0)
10165         {
10166           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10167           ChangeCount[x][y] = 0;        // allow at least one more change
10168
10169           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10170           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10171         }
10172       }
10173
10174       break;
10175     }
10176
10177     case CA_SET_CE_SCORE:
10178     {
10179       int last_ce_score = ei->collect_score;
10180
10181       ei->collect_score = action_arg_number_new;
10182
10183       if (ei->collect_score != last_ce_score)
10184       {
10185         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10186         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10187
10188         if (ei->collect_score == 0)
10189         {
10190           int xx, yy;
10191
10192           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10193           ChangeCount[x][y] = 0;        // allow at least one more change
10194
10195           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10196           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10197
10198           /*
10199             This is a very special case that seems to be a mixture between
10200             CheckElementChange() and CheckTriggeredElementChange(): while
10201             the first one only affects single elements that are triggered
10202             directly, the second one affects multiple elements in the playfield
10203             that are triggered indirectly by another element. This is a third
10204             case: Changing the CE score always affects multiple identical CEs,
10205             so every affected CE must be checked, not only the single CE for
10206             which the CE score was changed in the first place (as every instance
10207             of that CE shares the same CE score, and therefore also can change)!
10208           */
10209           SCAN_PLAYFIELD(xx, yy)
10210           {
10211             if (Feld[xx][yy] == element)
10212               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10213                                  CE_SCORE_GETS_ZERO);
10214           }
10215         }
10216       }
10217
10218       break;
10219     }
10220
10221     case CA_SET_CE_ARTWORK:
10222     {
10223       int artwork_element = action_arg_element;
10224       boolean reset_frame = FALSE;
10225       int xx, yy;
10226
10227       if (action_arg == CA_ARG_ELEMENT_RESET)
10228         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10229                            element);
10230
10231       if (ei->gfx_element != artwork_element)
10232         reset_frame = TRUE;
10233
10234       ei->gfx_element = artwork_element;
10235
10236       SCAN_PLAYFIELD(xx, yy)
10237       {
10238         if (Feld[xx][yy] == element)
10239         {
10240           if (reset_frame)
10241           {
10242             ResetGfxAnimation(xx, yy);
10243             ResetRandomAnimationValue(xx, yy);
10244           }
10245
10246           TEST_DrawLevelField(xx, yy);
10247         }
10248       }
10249
10250       break;
10251     }
10252
10253     // ---------- engine actions  ---------------------------------------------
10254
10255     case CA_SET_ENGINE_SCAN_MODE:
10256     {
10257       InitPlayfieldScanMode(action_arg);
10258
10259       break;
10260     }
10261
10262     default:
10263       break;
10264   }
10265 }
10266
10267 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10268 {
10269   int old_element = Feld[x][y];
10270   int new_element = GetElementFromGroupElement(element);
10271   int previous_move_direction = MovDir[x][y];
10272   int last_ce_value = CustomValue[x][y];
10273   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10274   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10275   boolean add_player_onto_element = (new_element_is_player &&
10276                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10277                                      IS_WALKABLE(old_element));
10278
10279   if (!add_player_onto_element)
10280   {
10281     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10282       RemoveMovingField(x, y);
10283     else
10284       RemoveField(x, y);
10285
10286     Feld[x][y] = new_element;
10287
10288     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10289       MovDir[x][y] = previous_move_direction;
10290
10291     if (element_info[new_element].use_last_ce_value)
10292       CustomValue[x][y] = last_ce_value;
10293
10294     InitField_WithBug1(x, y, FALSE);
10295
10296     new_element = Feld[x][y];   // element may have changed
10297
10298     ResetGfxAnimation(x, y);
10299     ResetRandomAnimationValue(x, y);
10300
10301     TEST_DrawLevelField(x, y);
10302
10303     if (GFX_CRUMBLED(new_element))
10304       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10305   }
10306
10307   // check if element under the player changes from accessible to unaccessible
10308   // (needed for special case of dropping element which then changes)
10309   // (must be checked after creating new element for walkable group elements)
10310   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10311       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10312   {
10313     Bang(x, y);
10314
10315     return;
10316   }
10317
10318   // "ChangeCount" not set yet to allow "entered by player" change one time
10319   if (new_element_is_player)
10320     RelocatePlayer(x, y, new_element);
10321
10322   if (is_change)
10323     ChangeCount[x][y]++;        // count number of changes in the same frame
10324
10325   TestIfBadThingTouchesPlayer(x, y);
10326   TestIfPlayerTouchesCustomElement(x, y);
10327   TestIfElementTouchesCustomElement(x, y);
10328 }
10329
10330 static void CreateField(int x, int y, int element)
10331 {
10332   CreateFieldExt(x, y, element, FALSE);
10333 }
10334
10335 static void CreateElementFromChange(int x, int y, int element)
10336 {
10337   element = GET_VALID_RUNTIME_ELEMENT(element);
10338
10339   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10340   {
10341     int old_element = Feld[x][y];
10342
10343     // prevent changed element from moving in same engine frame
10344     // unless both old and new element can either fall or move
10345     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10346         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10347       Stop[x][y] = TRUE;
10348   }
10349
10350   CreateFieldExt(x, y, element, TRUE);
10351 }
10352
10353 static boolean ChangeElement(int x, int y, int element, int page)
10354 {
10355   struct ElementInfo *ei = &element_info[element];
10356   struct ElementChangeInfo *change = &ei->change_page[page];
10357   int ce_value = CustomValue[x][y];
10358   int ce_score = ei->collect_score;
10359   int target_element;
10360   int old_element = Feld[x][y];
10361
10362   // always use default change event to prevent running into a loop
10363   if (ChangeEvent[x][y] == -1)
10364     ChangeEvent[x][y] = CE_DELAY;
10365
10366   if (ChangeEvent[x][y] == CE_DELAY)
10367   {
10368     // reset actual trigger element, trigger player and action element
10369     change->actual_trigger_element = EL_EMPTY;
10370     change->actual_trigger_player = EL_EMPTY;
10371     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10372     change->actual_trigger_side = CH_SIDE_NONE;
10373     change->actual_trigger_ce_value = 0;
10374     change->actual_trigger_ce_score = 0;
10375   }
10376
10377   // do not change elements more than a specified maximum number of changes
10378   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10379     return FALSE;
10380
10381   ChangeCount[x][y]++;          // count number of changes in the same frame
10382
10383   if (change->explode)
10384   {
10385     Bang(x, y);
10386
10387     return TRUE;
10388   }
10389
10390   if (change->use_target_content)
10391   {
10392     boolean complete_replace = TRUE;
10393     boolean can_replace[3][3];
10394     int xx, yy;
10395
10396     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10397     {
10398       boolean is_empty;
10399       boolean is_walkable;
10400       boolean is_diggable;
10401       boolean is_collectible;
10402       boolean is_removable;
10403       boolean is_destructible;
10404       int ex = x + xx - 1;
10405       int ey = y + yy - 1;
10406       int content_element = change->target_content.e[xx][yy];
10407       int e;
10408
10409       can_replace[xx][yy] = TRUE;
10410
10411       if (ex == x && ey == y)   // do not check changing element itself
10412         continue;
10413
10414       if (content_element == EL_EMPTY_SPACE)
10415       {
10416         can_replace[xx][yy] = FALSE;    // do not replace border with space
10417
10418         continue;
10419       }
10420
10421       if (!IN_LEV_FIELD(ex, ey))
10422       {
10423         can_replace[xx][yy] = FALSE;
10424         complete_replace = FALSE;
10425
10426         continue;
10427       }
10428
10429       e = Feld[ex][ey];
10430
10431       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10432         e = MovingOrBlocked2Element(ex, ey);
10433
10434       is_empty = (IS_FREE(ex, ey) ||
10435                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10436
10437       is_walkable     = (is_empty || IS_WALKABLE(e));
10438       is_diggable     = (is_empty || IS_DIGGABLE(e));
10439       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10440       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10441       is_removable    = (is_diggable || is_collectible);
10442
10443       can_replace[xx][yy] =
10444         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10445           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10446           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10447           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10448           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10449           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10450          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10451
10452       if (!can_replace[xx][yy])
10453         complete_replace = FALSE;
10454     }
10455
10456     if (!change->only_if_complete || complete_replace)
10457     {
10458       boolean something_has_changed = FALSE;
10459
10460       if (change->only_if_complete && change->use_random_replace &&
10461           RND(100) < change->random_percentage)
10462         return FALSE;
10463
10464       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10465       {
10466         int ex = x + xx - 1;
10467         int ey = y + yy - 1;
10468         int content_element;
10469
10470         if (can_replace[xx][yy] && (!change->use_random_replace ||
10471                                     RND(100) < change->random_percentage))
10472         {
10473           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10474             RemoveMovingField(ex, ey);
10475
10476           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10477
10478           content_element = change->target_content.e[xx][yy];
10479           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10480                                               ce_value, ce_score);
10481
10482           CreateElementFromChange(ex, ey, target_element);
10483
10484           something_has_changed = TRUE;
10485
10486           // for symmetry reasons, freeze newly created border elements
10487           if (ex != x || ey != y)
10488             Stop[ex][ey] = TRUE;        // no more moving in this frame
10489         }
10490       }
10491
10492       if (something_has_changed)
10493       {
10494         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10495         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10496       }
10497     }
10498   }
10499   else
10500   {
10501     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10502                                         ce_value, ce_score);
10503
10504     if (element == EL_DIAGONAL_GROWING ||
10505         element == EL_DIAGONAL_SHRINKING)
10506     {
10507       target_element = Store[x][y];
10508
10509       Store[x][y] = EL_EMPTY;
10510     }
10511
10512     CreateElementFromChange(x, y, target_element);
10513
10514     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10515     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10516   }
10517
10518   // this uses direct change before indirect change
10519   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10520
10521   return TRUE;
10522 }
10523
10524 static void HandleElementChange(int x, int y, int page)
10525 {
10526   int element = MovingOrBlocked2Element(x, y);
10527   struct ElementInfo *ei = &element_info[element];
10528   struct ElementChangeInfo *change = &ei->change_page[page];
10529   boolean handle_action_before_change = FALSE;
10530
10531 #ifdef DEBUG
10532   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10533       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10534   {
10535     printf("\n\n");
10536     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10537            x, y, element, element_info[element].token_name);
10538     printf("HandleElementChange(): This should never happen!\n");
10539     printf("\n\n");
10540   }
10541 #endif
10542
10543   // this can happen with classic bombs on walkable, changing elements
10544   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10545   {
10546     return;
10547   }
10548
10549   if (ChangeDelay[x][y] == 0)           // initialize element change
10550   {
10551     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10552
10553     if (change->can_change)
10554     {
10555       // !!! not clear why graphic animation should be reset at all here !!!
10556       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10557       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10558
10559       /*
10560         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10561
10562         When using an animation frame delay of 1 (this only happens with
10563         "sp_zonk.moving.left/right" in the classic graphics), the default
10564         (non-moving) animation shows wrong animation frames (while the
10565         moving animation, like "sp_zonk.moving.left/right", is correct,
10566         so this graphical bug never shows up with the classic graphics).
10567         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10568         be drawn instead of the correct frames 0,1,2,3. This is caused by
10569         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10570         an element change: First when the change delay ("ChangeDelay[][]")
10571         counter has reached zero after decrementing, then a second time in
10572         the next frame (after "GfxFrame[][]" was already incremented) when
10573         "ChangeDelay[][]" is reset to the initial delay value again.
10574
10575         This causes frame 0 to be drawn twice, while the last frame won't
10576         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10577
10578         As some animations may already be cleverly designed around this bug
10579         (at least the "Snake Bite" snake tail animation does this), it cannot
10580         simply be fixed here without breaking such existing animations.
10581         Unfortunately, it cannot easily be detected if a graphics set was
10582         designed "before" or "after" the bug was fixed. As a workaround,
10583         a new graphics set option "game.graphics_engine_version" was added
10584         to be able to specify the game's major release version for which the
10585         graphics set was designed, which can then be used to decide if the
10586         bugfix should be used (version 4 and above) or not (version 3 or
10587         below, or if no version was specified at all, as with old sets).
10588
10589         (The wrong/fixed animation frames can be tested with the test level set
10590         "test_gfxframe" and level "000", which contains a specially prepared
10591         custom element at level position (x/y) == (11/9) which uses the zonk
10592         animation mentioned above. Using "game.graphics_engine_version: 4"
10593         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10594         This can also be seen from the debug output for this test element.)
10595       */
10596
10597       // when a custom element is about to change (for example by change delay),
10598       // do not reset graphic animation when the custom element is moving
10599       if (game.graphics_engine_version < 4 &&
10600           !IS_MOVING(x, y))
10601       {
10602         ResetGfxAnimation(x, y);
10603         ResetRandomAnimationValue(x, y);
10604       }
10605
10606       if (change->pre_change_function)
10607         change->pre_change_function(x, y);
10608     }
10609   }
10610
10611   ChangeDelay[x][y]--;
10612
10613   if (ChangeDelay[x][y] != 0)           // continue element change
10614   {
10615     if (change->can_change)
10616     {
10617       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10618
10619       if (IS_ANIMATED(graphic))
10620         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10621
10622       if (change->change_function)
10623         change->change_function(x, y);
10624     }
10625   }
10626   else                                  // finish element change
10627   {
10628     if (ChangePage[x][y] != -1)         // remember page from delayed change
10629     {
10630       page = ChangePage[x][y];
10631       ChangePage[x][y] = -1;
10632
10633       change = &ei->change_page[page];
10634     }
10635
10636     if (IS_MOVING(x, y))                // never change a running system ;-)
10637     {
10638       ChangeDelay[x][y] = 1;            // try change after next move step
10639       ChangePage[x][y] = page;          // remember page to use for change
10640
10641       return;
10642     }
10643
10644     // special case: set new level random seed before changing element
10645     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10646       handle_action_before_change = TRUE;
10647
10648     if (change->has_action && handle_action_before_change)
10649       ExecuteCustomElementAction(x, y, element, page);
10650
10651     if (change->can_change)
10652     {
10653       if (ChangeElement(x, y, element, page))
10654       {
10655         if (change->post_change_function)
10656           change->post_change_function(x, y);
10657       }
10658     }
10659
10660     if (change->has_action && !handle_action_before_change)
10661       ExecuteCustomElementAction(x, y, element, page);
10662   }
10663 }
10664
10665 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10666                                               int trigger_element,
10667                                               int trigger_event,
10668                                               int trigger_player,
10669                                               int trigger_side,
10670                                               int trigger_page)
10671 {
10672   boolean change_done_any = FALSE;
10673   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10674   int i;
10675
10676   if (!(trigger_events[trigger_element][trigger_event]))
10677     return FALSE;
10678
10679   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10680
10681   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10682   {
10683     int element = EL_CUSTOM_START + i;
10684     boolean change_done = FALSE;
10685     int p;
10686
10687     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10688         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10689       continue;
10690
10691     for (p = 0; p < element_info[element].num_change_pages; p++)
10692     {
10693       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10694
10695       if (change->can_change_or_has_action &&
10696           change->has_event[trigger_event] &&
10697           change->trigger_side & trigger_side &&
10698           change->trigger_player & trigger_player &&
10699           change->trigger_page & trigger_page_bits &&
10700           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10701       {
10702         change->actual_trigger_element = trigger_element;
10703         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10704         change->actual_trigger_player_bits = trigger_player;
10705         change->actual_trigger_side = trigger_side;
10706         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10707         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10708
10709         if ((change->can_change && !change_done) || change->has_action)
10710         {
10711           int x, y;
10712
10713           SCAN_PLAYFIELD(x, y)
10714           {
10715             if (Feld[x][y] == element)
10716             {
10717               if (change->can_change && !change_done)
10718               {
10719                 // if element already changed in this frame, not only prevent
10720                 // another element change (checked in ChangeElement()), but
10721                 // also prevent additional element actions for this element
10722
10723                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10724                     !level.use_action_after_change_bug)
10725                   continue;
10726
10727                 ChangeDelay[x][y] = 1;
10728                 ChangeEvent[x][y] = trigger_event;
10729
10730                 HandleElementChange(x, y, p);
10731               }
10732               else if (change->has_action)
10733               {
10734                 // if element already changed in this frame, not only prevent
10735                 // another element change (checked in ChangeElement()), but
10736                 // also prevent additional element actions for this element
10737
10738                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10739                     !level.use_action_after_change_bug)
10740                   continue;
10741
10742                 ExecuteCustomElementAction(x, y, element, p);
10743                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10744               }
10745             }
10746           }
10747
10748           if (change->can_change)
10749           {
10750             change_done = TRUE;
10751             change_done_any = TRUE;
10752           }
10753         }
10754       }
10755     }
10756   }
10757
10758   RECURSION_LOOP_DETECTION_END();
10759
10760   return change_done_any;
10761 }
10762
10763 static boolean CheckElementChangeExt(int x, int y,
10764                                      int element,
10765                                      int trigger_element,
10766                                      int trigger_event,
10767                                      int trigger_player,
10768                                      int trigger_side)
10769 {
10770   boolean change_done = FALSE;
10771   int p;
10772
10773   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10774       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10775     return FALSE;
10776
10777   if (Feld[x][y] == EL_BLOCKED)
10778   {
10779     Blocked2Moving(x, y, &x, &y);
10780     element = Feld[x][y];
10781   }
10782
10783   // check if element has already changed or is about to change after moving
10784   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10785        Feld[x][y] != element) ||
10786
10787       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10788        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10789         ChangePage[x][y] != -1)))
10790     return FALSE;
10791
10792   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10793
10794   for (p = 0; p < element_info[element].num_change_pages; p++)
10795   {
10796     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10797
10798     /* check trigger element for all events where the element that is checked
10799        for changing interacts with a directly adjacent element -- this is
10800        different to element changes that affect other elements to change on the
10801        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10802     boolean check_trigger_element =
10803       (trigger_event == CE_TOUCHING_X ||
10804        trigger_event == CE_HITTING_X ||
10805        trigger_event == CE_HIT_BY_X ||
10806        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10807
10808     if (change->can_change_or_has_action &&
10809         change->has_event[trigger_event] &&
10810         change->trigger_side & trigger_side &&
10811         change->trigger_player & trigger_player &&
10812         (!check_trigger_element ||
10813          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10814     {
10815       change->actual_trigger_element = trigger_element;
10816       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10817       change->actual_trigger_player_bits = trigger_player;
10818       change->actual_trigger_side = trigger_side;
10819       change->actual_trigger_ce_value = CustomValue[x][y];
10820       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10821
10822       // special case: trigger element not at (x,y) position for some events
10823       if (check_trigger_element)
10824       {
10825         static struct
10826         {
10827           int dx, dy;
10828         } move_xy[] =
10829           {
10830             {  0,  0 },
10831             { -1,  0 },
10832             { +1,  0 },
10833             {  0,  0 },
10834             {  0, -1 },
10835             {  0,  0 }, { 0, 0 }, { 0, 0 },
10836             {  0, +1 }
10837           };
10838
10839         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10840         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10841
10842         change->actual_trigger_ce_value = CustomValue[xx][yy];
10843         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10844       }
10845
10846       if (change->can_change && !change_done)
10847       {
10848         ChangeDelay[x][y] = 1;
10849         ChangeEvent[x][y] = trigger_event;
10850
10851         HandleElementChange(x, y, p);
10852
10853         change_done = TRUE;
10854       }
10855       else if (change->has_action)
10856       {
10857         ExecuteCustomElementAction(x, y, element, p);
10858         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10859       }
10860     }
10861   }
10862
10863   RECURSION_LOOP_DETECTION_END();
10864
10865   return change_done;
10866 }
10867
10868 static void PlayPlayerSound(struct PlayerInfo *player)
10869 {
10870   int jx = player->jx, jy = player->jy;
10871   int sound_element = player->artwork_element;
10872   int last_action = player->last_action_waiting;
10873   int action = player->action_waiting;
10874
10875   if (player->is_waiting)
10876   {
10877     if (action != last_action)
10878       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10879     else
10880       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10881   }
10882   else
10883   {
10884     if (action != last_action)
10885       StopSound(element_info[sound_element].sound[last_action]);
10886
10887     if (last_action == ACTION_SLEEPING)
10888       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10889   }
10890 }
10891
10892 static void PlayAllPlayersSound(void)
10893 {
10894   int i;
10895
10896   for (i = 0; i < MAX_PLAYERS; i++)
10897     if (stored_player[i].active)
10898       PlayPlayerSound(&stored_player[i]);
10899 }
10900
10901 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10902 {
10903   boolean last_waiting = player->is_waiting;
10904   int move_dir = player->MovDir;
10905
10906   player->dir_waiting = move_dir;
10907   player->last_action_waiting = player->action_waiting;
10908
10909   if (is_waiting)
10910   {
10911     if (!last_waiting)          // not waiting -> waiting
10912     {
10913       player->is_waiting = TRUE;
10914
10915       player->frame_counter_bored =
10916         FrameCounter +
10917         game.player_boring_delay_fixed +
10918         GetSimpleRandom(game.player_boring_delay_random);
10919       player->frame_counter_sleeping =
10920         FrameCounter +
10921         game.player_sleeping_delay_fixed +
10922         GetSimpleRandom(game.player_sleeping_delay_random);
10923
10924       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10925     }
10926
10927     if (game.player_sleeping_delay_fixed +
10928         game.player_sleeping_delay_random > 0 &&
10929         player->anim_delay_counter == 0 &&
10930         player->post_delay_counter == 0 &&
10931         FrameCounter >= player->frame_counter_sleeping)
10932       player->is_sleeping = TRUE;
10933     else if (game.player_boring_delay_fixed +
10934              game.player_boring_delay_random > 0 &&
10935              FrameCounter >= player->frame_counter_bored)
10936       player->is_bored = TRUE;
10937
10938     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10939                               player->is_bored ? ACTION_BORING :
10940                               ACTION_WAITING);
10941
10942     if (player->is_sleeping && player->use_murphy)
10943     {
10944       // special case for sleeping Murphy when leaning against non-free tile
10945
10946       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10947           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10948            !IS_MOVING(player->jx - 1, player->jy)))
10949         move_dir = MV_LEFT;
10950       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10951                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10952                 !IS_MOVING(player->jx + 1, player->jy)))
10953         move_dir = MV_RIGHT;
10954       else
10955         player->is_sleeping = FALSE;
10956
10957       player->dir_waiting = move_dir;
10958     }
10959
10960     if (player->is_sleeping)
10961     {
10962       if (player->num_special_action_sleeping > 0)
10963       {
10964         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10965         {
10966           int last_special_action = player->special_action_sleeping;
10967           int num_special_action = player->num_special_action_sleeping;
10968           int special_action =
10969             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10970              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10971              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10972              last_special_action + 1 : ACTION_SLEEPING);
10973           int special_graphic =
10974             el_act_dir2img(player->artwork_element, special_action, move_dir);
10975
10976           player->anim_delay_counter =
10977             graphic_info[special_graphic].anim_delay_fixed +
10978             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10979           player->post_delay_counter =
10980             graphic_info[special_graphic].post_delay_fixed +
10981             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10982
10983           player->special_action_sleeping = special_action;
10984         }
10985
10986         if (player->anim_delay_counter > 0)
10987         {
10988           player->action_waiting = player->special_action_sleeping;
10989           player->anim_delay_counter--;
10990         }
10991         else if (player->post_delay_counter > 0)
10992         {
10993           player->post_delay_counter--;
10994         }
10995       }
10996     }
10997     else if (player->is_bored)
10998     {
10999       if (player->num_special_action_bored > 0)
11000       {
11001         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11002         {
11003           int special_action =
11004             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11005           int special_graphic =
11006             el_act_dir2img(player->artwork_element, special_action, move_dir);
11007
11008           player->anim_delay_counter =
11009             graphic_info[special_graphic].anim_delay_fixed +
11010             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11011           player->post_delay_counter =
11012             graphic_info[special_graphic].post_delay_fixed +
11013             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11014
11015           player->special_action_bored = special_action;
11016         }
11017
11018         if (player->anim_delay_counter > 0)
11019         {
11020           player->action_waiting = player->special_action_bored;
11021           player->anim_delay_counter--;
11022         }
11023         else if (player->post_delay_counter > 0)
11024         {
11025           player->post_delay_counter--;
11026         }
11027       }
11028     }
11029   }
11030   else if (last_waiting)        // waiting -> not waiting
11031   {
11032     player->is_waiting = FALSE;
11033     player->is_bored = FALSE;
11034     player->is_sleeping = FALSE;
11035
11036     player->frame_counter_bored = -1;
11037     player->frame_counter_sleeping = -1;
11038
11039     player->anim_delay_counter = 0;
11040     player->post_delay_counter = 0;
11041
11042     player->dir_waiting = player->MovDir;
11043     player->action_waiting = ACTION_DEFAULT;
11044
11045     player->special_action_bored = ACTION_DEFAULT;
11046     player->special_action_sleeping = ACTION_DEFAULT;
11047   }
11048 }
11049
11050 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11051 {
11052   if ((!player->is_moving  && player->was_moving) ||
11053       (player->MovPos == 0 && player->was_moving) ||
11054       (player->is_snapping && !player->was_snapping) ||
11055       (player->is_dropping && !player->was_dropping))
11056   {
11057     if (!CheckSaveEngineSnapshotToList())
11058       return;
11059
11060     player->was_moving = FALSE;
11061     player->was_snapping = TRUE;
11062     player->was_dropping = TRUE;
11063   }
11064   else
11065   {
11066     if (player->is_moving)
11067       player->was_moving = TRUE;
11068
11069     if (!player->is_snapping)
11070       player->was_snapping = FALSE;
11071
11072     if (!player->is_dropping)
11073       player->was_dropping = FALSE;
11074   }
11075 }
11076
11077 static void CheckSingleStepMode(struct PlayerInfo *player)
11078 {
11079   if (tape.single_step && tape.recording && !tape.pausing)
11080   {
11081     /* as it is called "single step mode", just return to pause mode when the
11082        player stopped moving after one tile (or never starts moving at all) */
11083     if (!player->is_moving &&
11084         !player->is_pushing &&
11085         !player->is_dropping_pressed)
11086       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11087   }
11088
11089   CheckSaveEngineSnapshot(player);
11090 }
11091
11092 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11093 {
11094   int left      = player_action & JOY_LEFT;
11095   int right     = player_action & JOY_RIGHT;
11096   int up        = player_action & JOY_UP;
11097   int down      = player_action & JOY_DOWN;
11098   int button1   = player_action & JOY_BUTTON_1;
11099   int button2   = player_action & JOY_BUTTON_2;
11100   int dx        = (left ? -1 : right ? 1 : 0);
11101   int dy        = (up   ? -1 : down  ? 1 : 0);
11102
11103   if (!player->active || tape.pausing)
11104     return 0;
11105
11106   if (player_action)
11107   {
11108     if (button1)
11109       SnapField(player, dx, dy);
11110     else
11111     {
11112       if (button2)
11113         DropElement(player);
11114
11115       MovePlayer(player, dx, dy);
11116     }
11117
11118     CheckSingleStepMode(player);
11119
11120     SetPlayerWaiting(player, FALSE);
11121
11122     return player_action;
11123   }
11124   else
11125   {
11126     // no actions for this player (no input at player's configured device)
11127
11128     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11129     SnapField(player, 0, 0);
11130     CheckGravityMovementWhenNotMoving(player);
11131
11132     if (player->MovPos == 0)
11133       SetPlayerWaiting(player, TRUE);
11134
11135     if (player->MovPos == 0)    // needed for tape.playing
11136       player->is_moving = FALSE;
11137
11138     player->is_dropping = FALSE;
11139     player->is_dropping_pressed = FALSE;
11140     player->drop_pressed_delay = 0;
11141
11142     CheckSingleStepMode(player);
11143
11144     return 0;
11145   }
11146 }
11147
11148 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11149                                          byte *tape_action)
11150 {
11151   if (!tape.use_mouse)
11152     return;
11153
11154   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11155   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11156   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11157 }
11158
11159 static void SetTapeActionFromMouseAction(byte *tape_action,
11160                                          struct MouseActionInfo *mouse_action)
11161 {
11162   if (!tape.use_mouse)
11163     return;
11164
11165   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11166   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11167   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11168 }
11169
11170 static void CheckLevelSolved(void)
11171 {
11172   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11173   {
11174     if (game_em.level_solved &&
11175         !game_em.game_over)                             // game won
11176     {
11177       LevelSolved();
11178
11179       game_em.game_over = TRUE;
11180
11181       game.all_players_gone = TRUE;
11182     }
11183
11184     if (game_em.game_over)                              // game lost
11185       game.all_players_gone = TRUE;
11186   }
11187   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11188   {
11189     if (game_sp.level_solved &&
11190         !game_sp.game_over)                             // game won
11191     {
11192       LevelSolved();
11193
11194       game_sp.game_over = TRUE;
11195
11196       game.all_players_gone = TRUE;
11197     }
11198
11199     if (game_sp.game_over)                              // game lost
11200       game.all_players_gone = TRUE;
11201   }
11202   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11203   {
11204     if (game_mm.level_solved &&
11205         !game_mm.game_over)                             // game won
11206     {
11207       LevelSolved();
11208
11209       game_mm.game_over = TRUE;
11210
11211       game.all_players_gone = TRUE;
11212     }
11213
11214     if (game_mm.game_over)                              // game lost
11215       game.all_players_gone = TRUE;
11216   }
11217 }
11218
11219 static void CheckLevelTime(void)
11220 {
11221   int i;
11222
11223   if (TimeFrames >= FRAMES_PER_SECOND)
11224   {
11225     TimeFrames = 0;
11226     TapeTime++;
11227
11228     for (i = 0; i < MAX_PLAYERS; i++)
11229     {
11230       struct PlayerInfo *player = &stored_player[i];
11231
11232       if (SHIELD_ON(player))
11233       {
11234         player->shield_normal_time_left--;
11235
11236         if (player->shield_deadly_time_left > 0)
11237           player->shield_deadly_time_left--;
11238       }
11239     }
11240
11241     if (!game.LevelSolved && !level.use_step_counter)
11242     {
11243       TimePlayed++;
11244
11245       if (TimeLeft > 0)
11246       {
11247         TimeLeft--;
11248
11249         if (TimeLeft <= 10 && setup.time_limit)
11250           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11251
11252         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11253            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11254
11255         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11256
11257         if (!TimeLeft && setup.time_limit)
11258         {
11259           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11260             game_em.lev->killed_out_of_time = TRUE;
11261           else
11262             for (i = 0; i < MAX_PLAYERS; i++)
11263               KillPlayer(&stored_player[i]);
11264         }
11265       }
11266       else if (game.no_time_limit && !game.all_players_gone)
11267       {
11268         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11269       }
11270
11271       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11272     }
11273
11274     if (tape.recording || tape.playing)
11275       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11276   }
11277
11278   if (tape.recording || tape.playing)
11279     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11280
11281   UpdateAndDisplayGameControlValues();
11282 }
11283
11284 void AdvanceFrameAndPlayerCounters(int player_nr)
11285 {
11286   int i;
11287
11288   // advance frame counters (global frame counter and time frame counter)
11289   FrameCounter++;
11290   TimeFrames++;
11291
11292   // advance player counters (counters for move delay, move animation etc.)
11293   for (i = 0; i < MAX_PLAYERS; i++)
11294   {
11295     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11296     int move_delay_value = stored_player[i].move_delay_value;
11297     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11298
11299     if (!advance_player_counters)       // not all players may be affected
11300       continue;
11301
11302     if (move_frames == 0)       // less than one move per game frame
11303     {
11304       int stepsize = TILEX / move_delay_value;
11305       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11306       int count = (stored_player[i].is_moving ?
11307                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11308
11309       if (count % delay == 0)
11310         move_frames = 1;
11311     }
11312
11313     stored_player[i].Frame += move_frames;
11314
11315     if (stored_player[i].MovPos != 0)
11316       stored_player[i].StepFrame += move_frames;
11317
11318     if (stored_player[i].move_delay > 0)
11319       stored_player[i].move_delay--;
11320
11321     // due to bugs in previous versions, counter must count up, not down
11322     if (stored_player[i].push_delay != -1)
11323       stored_player[i].push_delay++;
11324
11325     if (stored_player[i].drop_delay > 0)
11326       stored_player[i].drop_delay--;
11327
11328     if (stored_player[i].is_dropping_pressed)
11329       stored_player[i].drop_pressed_delay++;
11330   }
11331 }
11332
11333 void StartGameActions(boolean init_network_game, boolean record_tape,
11334                       int random_seed)
11335 {
11336   unsigned int new_random_seed = InitRND(random_seed);
11337
11338   if (record_tape)
11339     TapeStartRecording(new_random_seed);
11340
11341   if (init_network_game)
11342   {
11343     SendToServer_LevelFile();
11344     SendToServer_StartPlaying();
11345
11346     return;
11347   }
11348
11349   InitGame();
11350 }
11351
11352 static void GameActionsExt(void)
11353 {
11354 #if 0
11355   static unsigned int game_frame_delay = 0;
11356 #endif
11357   unsigned int game_frame_delay_value;
11358   byte *recorded_player_action;
11359   byte summarized_player_action = 0;
11360   byte tape_action[MAX_PLAYERS];
11361   int i;
11362
11363   // detect endless loops, caused by custom element programming
11364   if (recursion_loop_detected && recursion_loop_depth == 0)
11365   {
11366     char *message = getStringCat3("Internal Error! Element ",
11367                                   EL_NAME(recursion_loop_element),
11368                                   " caused endless loop! Quit the game?");
11369
11370     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11371           EL_NAME(recursion_loop_element));
11372
11373     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11374
11375     recursion_loop_detected = FALSE;    // if game should be continued
11376
11377     free(message);
11378
11379     return;
11380   }
11381
11382   if (game.restart_level)
11383     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11384
11385   CheckLevelSolved();
11386
11387   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11388     GameWon();
11389
11390   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11391     TapeStop();
11392
11393   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11394     return;
11395
11396   game_frame_delay_value =
11397     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11398
11399   if (tape.playing && tape.warp_forward && !tape.pausing)
11400     game_frame_delay_value = 0;
11401
11402   SetVideoFrameDelay(game_frame_delay_value);
11403
11404   // (de)activate virtual buttons depending on current game status
11405   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11406   {
11407     if (game.all_players_gone)  // if no players there to be controlled anymore
11408       SetOverlayActive(FALSE);
11409     else if (!tape.playing)     // if game continues after tape stopped playing
11410       SetOverlayActive(TRUE);
11411   }
11412
11413 #if 0
11414 #if 0
11415   // ---------- main game synchronization point ----------
11416
11417   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11418
11419   printf("::: skip == %d\n", skip);
11420
11421 #else
11422   // ---------- main game synchronization point ----------
11423
11424   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11425 #endif
11426 #endif
11427
11428   if (network_playing && !network_player_action_received)
11429   {
11430     // try to get network player actions in time
11431
11432     // last chance to get network player actions without main loop delay
11433     HandleNetworking();
11434
11435     // game was quit by network peer
11436     if (game_status != GAME_MODE_PLAYING)
11437       return;
11438
11439     // check if network player actions still missing and game still running
11440     if (!network_player_action_received && !checkGameEnded())
11441       return;           // failed to get network player actions in time
11442
11443     // do not yet reset "network_player_action_received" (for tape.pausing)
11444   }
11445
11446   if (tape.pausing)
11447     return;
11448
11449   // at this point we know that we really continue executing the game
11450
11451   network_player_action_received = FALSE;
11452
11453   // when playing tape, read previously recorded player input from tape data
11454   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11455
11456   local_player->effective_mouse_action = local_player->mouse_action;
11457
11458   if (recorded_player_action != NULL)
11459     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11460                                  recorded_player_action);
11461
11462   // TapePlayAction() may return NULL when toggling to "pause before death"
11463   if (tape.pausing)
11464     return;
11465
11466   if (tape.set_centered_player)
11467   {
11468     game.centered_player_nr_next = tape.centered_player_nr_next;
11469     game.set_centered_player = TRUE;
11470   }
11471
11472   for (i = 0; i < MAX_PLAYERS; i++)
11473   {
11474     summarized_player_action |= stored_player[i].action;
11475
11476     if (!network_playing && (game.team_mode || tape.playing))
11477       stored_player[i].effective_action = stored_player[i].action;
11478   }
11479
11480   if (network_playing && !checkGameEnded())
11481     SendToServer_MovePlayer(summarized_player_action);
11482
11483   // summarize all actions at local players mapped input device position
11484   // (this allows using different input devices in single player mode)
11485   if (!network.enabled && !game.team_mode)
11486     stored_player[map_player_action[local_player->index_nr]].effective_action =
11487       summarized_player_action;
11488
11489   // summarize all actions at centered player in local team mode
11490   if (tape.recording &&
11491       setup.team_mode && !network.enabled &&
11492       setup.input_on_focus &&
11493       game.centered_player_nr != -1)
11494   {
11495     for (i = 0; i < MAX_PLAYERS; i++)
11496       stored_player[map_player_action[i]].effective_action =
11497         (i == game.centered_player_nr ? summarized_player_action : 0);
11498   }
11499
11500   if (recorded_player_action != NULL)
11501     for (i = 0; i < MAX_PLAYERS; i++)
11502       stored_player[i].effective_action = recorded_player_action[i];
11503
11504   for (i = 0; i < MAX_PLAYERS; i++)
11505   {
11506     tape_action[i] = stored_player[i].effective_action;
11507
11508     /* (this may happen in the RND game engine if a player was not present on
11509        the playfield on level start, but appeared later from a custom element */
11510     if (setup.team_mode &&
11511         tape.recording &&
11512         tape_action[i] &&
11513         !tape.player_participates[i])
11514       tape.player_participates[i] = TRUE;
11515   }
11516
11517   SetTapeActionFromMouseAction(tape_action,
11518                                &local_player->effective_mouse_action);
11519
11520   // only record actions from input devices, but not programmed actions
11521   if (tape.recording)
11522     TapeRecordAction(tape_action);
11523
11524   // remember if game was played (especially after tape stopped playing)
11525   if (!tape.playing && summarized_player_action)
11526     game.GamePlayed = TRUE;
11527
11528 #if USE_NEW_PLAYER_ASSIGNMENTS
11529   // !!! also map player actions in single player mode !!!
11530   // if (game.team_mode)
11531   if (1)
11532   {
11533     byte mapped_action[MAX_PLAYERS];
11534
11535 #if DEBUG_PLAYER_ACTIONS
11536     printf(":::");
11537     for (i = 0; i < MAX_PLAYERS; i++)
11538       printf(" %d, ", stored_player[i].effective_action);
11539 #endif
11540
11541     for (i = 0; i < MAX_PLAYERS; i++)
11542       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11543
11544     for (i = 0; i < MAX_PLAYERS; i++)
11545       stored_player[i].effective_action = mapped_action[i];
11546
11547 #if DEBUG_PLAYER_ACTIONS
11548     printf(" =>");
11549     for (i = 0; i < MAX_PLAYERS; i++)
11550       printf(" %d, ", stored_player[i].effective_action);
11551     printf("\n");
11552 #endif
11553   }
11554 #if DEBUG_PLAYER_ACTIONS
11555   else
11556   {
11557     printf(":::");
11558     for (i = 0; i < MAX_PLAYERS; i++)
11559       printf(" %d, ", stored_player[i].effective_action);
11560     printf("\n");
11561   }
11562 #endif
11563 #endif
11564
11565   for (i = 0; i < MAX_PLAYERS; i++)
11566   {
11567     // allow engine snapshot in case of changed movement attempt
11568     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11569         (stored_player[i].effective_action & KEY_MOTION))
11570       game.snapshot.changed_action = TRUE;
11571
11572     // allow engine snapshot in case of snapping/dropping attempt
11573     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11574         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11575       game.snapshot.changed_action = TRUE;
11576
11577     game.snapshot.last_action[i] = stored_player[i].effective_action;
11578   }
11579
11580   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11581   {
11582     GameActions_EM_Main();
11583   }
11584   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11585   {
11586     GameActions_SP_Main();
11587   }
11588   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11589   {
11590     GameActions_MM_Main();
11591   }
11592   else
11593   {
11594     GameActions_RND_Main();
11595   }
11596
11597   BlitScreenToBitmap(backbuffer);
11598
11599   CheckLevelSolved();
11600   CheckLevelTime();
11601
11602   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11603
11604   if (global.show_frames_per_second)
11605   {
11606     static unsigned int fps_counter = 0;
11607     static int fps_frames = 0;
11608     unsigned int fps_delay_ms = Counter() - fps_counter;
11609
11610     fps_frames++;
11611
11612     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11613     {
11614       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11615
11616       fps_frames = 0;
11617       fps_counter = Counter();
11618
11619       // always draw FPS to screen after FPS value was updated
11620       redraw_mask |= REDRAW_FPS;
11621     }
11622
11623     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11624     if (GetDrawDeactivationMask() == REDRAW_NONE)
11625       redraw_mask |= REDRAW_FPS;
11626   }
11627 }
11628
11629 static void GameActions_CheckSaveEngineSnapshot(void)
11630 {
11631   if (!game.snapshot.save_snapshot)
11632     return;
11633
11634   // clear flag for saving snapshot _before_ saving snapshot
11635   game.snapshot.save_snapshot = FALSE;
11636
11637   SaveEngineSnapshotToList();
11638 }
11639
11640 void GameActions(void)
11641 {
11642   GameActionsExt();
11643
11644   GameActions_CheckSaveEngineSnapshot();
11645 }
11646
11647 void GameActions_EM_Main(void)
11648 {
11649   byte effective_action[MAX_PLAYERS];
11650   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11651   int i;
11652
11653   for (i = 0; i < MAX_PLAYERS; i++)
11654     effective_action[i] = stored_player[i].effective_action;
11655
11656   GameActions_EM(effective_action, warp_mode);
11657 }
11658
11659 void GameActions_SP_Main(void)
11660 {
11661   byte effective_action[MAX_PLAYERS];
11662   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11663   int i;
11664
11665   for (i = 0; i < MAX_PLAYERS; i++)
11666     effective_action[i] = stored_player[i].effective_action;
11667
11668   GameActions_SP(effective_action, warp_mode);
11669
11670   for (i = 0; i < MAX_PLAYERS; i++)
11671   {
11672     if (stored_player[i].force_dropping)
11673       stored_player[i].action |= KEY_BUTTON_DROP;
11674
11675     stored_player[i].force_dropping = FALSE;
11676   }
11677 }
11678
11679 void GameActions_MM_Main(void)
11680 {
11681   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11682
11683   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11684 }
11685
11686 void GameActions_RND_Main(void)
11687 {
11688   GameActions_RND();
11689 }
11690
11691 void GameActions_RND(void)
11692 {
11693   static struct MouseActionInfo mouse_action_last = { 0 };
11694   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11695   int magic_wall_x = 0, magic_wall_y = 0;
11696   int i, x, y, element, graphic, last_gfx_frame;
11697
11698   InitPlayfieldScanModeVars();
11699
11700   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11701   {
11702     SCAN_PLAYFIELD(x, y)
11703     {
11704       ChangeCount[x][y] = 0;
11705       ChangeEvent[x][y] = -1;
11706     }
11707   }
11708
11709   if (game.set_centered_player)
11710   {
11711     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11712
11713     // switching to "all players" only possible if all players fit to screen
11714     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11715     {
11716       game.centered_player_nr_next = game.centered_player_nr;
11717       game.set_centered_player = FALSE;
11718     }
11719
11720     // do not switch focus to non-existing (or non-active) player
11721     if (game.centered_player_nr_next >= 0 &&
11722         !stored_player[game.centered_player_nr_next].active)
11723     {
11724       game.centered_player_nr_next = game.centered_player_nr;
11725       game.set_centered_player = FALSE;
11726     }
11727   }
11728
11729   if (game.set_centered_player &&
11730       ScreenMovPos == 0)        // screen currently aligned at tile position
11731   {
11732     int sx, sy;
11733
11734     if (game.centered_player_nr_next == -1)
11735     {
11736       setScreenCenteredToAllPlayers(&sx, &sy);
11737     }
11738     else
11739     {
11740       sx = stored_player[game.centered_player_nr_next].jx;
11741       sy = stored_player[game.centered_player_nr_next].jy;
11742     }
11743
11744     game.centered_player_nr = game.centered_player_nr_next;
11745     game.set_centered_player = FALSE;
11746
11747     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11748     DrawGameDoorValues();
11749   }
11750
11751   for (i = 0; i < MAX_PLAYERS; i++)
11752   {
11753     int actual_player_action = stored_player[i].effective_action;
11754
11755 #if 1
11756     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11757        - rnd_equinox_tetrachloride 048
11758        - rnd_equinox_tetrachloride_ii 096
11759        - rnd_emanuel_schmieg 002
11760        - doctor_sloan_ww 001, 020
11761     */
11762     if (stored_player[i].MovPos == 0)
11763       CheckGravityMovement(&stored_player[i]);
11764 #endif
11765
11766     // overwrite programmed action with tape action
11767     if (stored_player[i].programmed_action)
11768       actual_player_action = stored_player[i].programmed_action;
11769
11770     PlayerActions(&stored_player[i], actual_player_action);
11771
11772     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11773   }
11774
11775   ScrollScreen(NULL, SCROLL_GO_ON);
11776
11777   /* for backwards compatibility, the following code emulates a fixed bug that
11778      occured when pushing elements (causing elements that just made their last
11779      pushing step to already (if possible) make their first falling step in the
11780      same game frame, which is bad); this code is also needed to use the famous
11781      "spring push bug" which is used in older levels and might be wanted to be
11782      used also in newer levels, but in this case the buggy pushing code is only
11783      affecting the "spring" element and no other elements */
11784
11785   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11786   {
11787     for (i = 0; i < MAX_PLAYERS; i++)
11788     {
11789       struct PlayerInfo *player = &stored_player[i];
11790       int x = player->jx;
11791       int y = player->jy;
11792
11793       if (player->active && player->is_pushing && player->is_moving &&
11794           IS_MOVING(x, y) &&
11795           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11796            Feld[x][y] == EL_SPRING))
11797       {
11798         ContinueMoving(x, y);
11799
11800         // continue moving after pushing (this is actually a bug)
11801         if (!IS_MOVING(x, y))
11802           Stop[x][y] = FALSE;
11803       }
11804     }
11805   }
11806
11807   SCAN_PLAYFIELD(x, y)
11808   {
11809     Last[x][y] = Feld[x][y];
11810
11811     ChangeCount[x][y] = 0;
11812     ChangeEvent[x][y] = -1;
11813
11814     // this must be handled before main playfield loop
11815     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11816     {
11817       MovDelay[x][y]--;
11818       if (MovDelay[x][y] <= 0)
11819         RemoveField(x, y);
11820     }
11821
11822     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11823     {
11824       MovDelay[x][y]--;
11825       if (MovDelay[x][y] <= 0)
11826       {
11827         RemoveField(x, y);
11828         TEST_DrawLevelField(x, y);
11829
11830         TestIfElementTouchesCustomElement(x, y);        // for empty space
11831       }
11832     }
11833
11834 #if DEBUG
11835     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11836     {
11837       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11838       printf("GameActions(): This should never happen!\n");
11839
11840       ChangePage[x][y] = -1;
11841     }
11842 #endif
11843
11844     Stop[x][y] = FALSE;
11845     if (WasJustMoving[x][y] > 0)
11846       WasJustMoving[x][y]--;
11847     if (WasJustFalling[x][y] > 0)
11848       WasJustFalling[x][y]--;
11849     if (CheckCollision[x][y] > 0)
11850       CheckCollision[x][y]--;
11851     if (CheckImpact[x][y] > 0)
11852       CheckImpact[x][y]--;
11853
11854     GfxFrame[x][y]++;
11855
11856     /* reset finished pushing action (not done in ContinueMoving() to allow
11857        continuous pushing animation for elements with zero push delay) */
11858     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11859     {
11860       ResetGfxAnimation(x, y);
11861       TEST_DrawLevelField(x, y);
11862     }
11863
11864 #if DEBUG
11865     if (IS_BLOCKED(x, y))
11866     {
11867       int oldx, oldy;
11868
11869       Blocked2Moving(x, y, &oldx, &oldy);
11870       if (!IS_MOVING(oldx, oldy))
11871       {
11872         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11873         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11874         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11875         printf("GameActions(): This should never happen!\n");
11876       }
11877     }
11878 #endif
11879   }
11880
11881   if (mouse_action.button)
11882   {
11883     int new_button = (mouse_action.button && mouse_action_last.button == 0);
11884
11885     x = local_player->mouse_action.lx;
11886     y = local_player->mouse_action.ly;
11887     element = Feld[x][y];
11888
11889     if (new_button)
11890     {
11891       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
11892       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
11893     }
11894
11895     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
11896     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
11897   }
11898
11899   SCAN_PLAYFIELD(x, y)
11900   {
11901     element = Feld[x][y];
11902     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11903     last_gfx_frame = GfxFrame[x][y];
11904
11905     ResetGfxFrame(x, y);
11906
11907     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11908       DrawLevelGraphicAnimation(x, y, graphic);
11909
11910     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11911         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11912       ResetRandomAnimationValue(x, y);
11913
11914     SetRandomAnimationValue(x, y);
11915
11916     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11917
11918     if (IS_INACTIVE(element))
11919     {
11920       if (IS_ANIMATED(graphic))
11921         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11922
11923       continue;
11924     }
11925
11926     // this may take place after moving, so 'element' may have changed
11927     if (IS_CHANGING(x, y) &&
11928         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11929     {
11930       int page = element_info[element].event_page_nr[CE_DELAY];
11931
11932       HandleElementChange(x, y, page);
11933
11934       element = Feld[x][y];
11935       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11936     }
11937
11938     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11939     {
11940       StartMoving(x, y);
11941
11942       element = Feld[x][y];
11943       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11944
11945       if (IS_ANIMATED(graphic) &&
11946           !IS_MOVING(x, y) &&
11947           !Stop[x][y])
11948         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11949
11950       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11951         TEST_DrawTwinkleOnField(x, y);
11952     }
11953     else if (element == EL_ACID)
11954     {
11955       if (!Stop[x][y])
11956         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11957     }
11958     else if ((element == EL_EXIT_OPEN ||
11959               element == EL_EM_EXIT_OPEN ||
11960               element == EL_SP_EXIT_OPEN ||
11961               element == EL_STEEL_EXIT_OPEN ||
11962               element == EL_EM_STEEL_EXIT_OPEN ||
11963               element == EL_SP_TERMINAL ||
11964               element == EL_SP_TERMINAL_ACTIVE ||
11965               element == EL_EXTRA_TIME ||
11966               element == EL_SHIELD_NORMAL ||
11967               element == EL_SHIELD_DEADLY) &&
11968              IS_ANIMATED(graphic))
11969       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11970     else if (IS_MOVING(x, y))
11971       ContinueMoving(x, y);
11972     else if (IS_ACTIVE_BOMB(element))
11973       CheckDynamite(x, y);
11974     else if (element == EL_AMOEBA_GROWING)
11975       AmoebeWaechst(x, y);
11976     else if (element == EL_AMOEBA_SHRINKING)
11977       AmoebaDisappearing(x, y);
11978
11979 #if !USE_NEW_AMOEBA_CODE
11980     else if (IS_AMOEBALIVE(element))
11981       AmoebeAbleger(x, y);
11982 #endif
11983
11984     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11985       Life(x, y);
11986     else if (element == EL_EXIT_CLOSED)
11987       CheckExit(x, y);
11988     else if (element == EL_EM_EXIT_CLOSED)
11989       CheckExitEM(x, y);
11990     else if (element == EL_STEEL_EXIT_CLOSED)
11991       CheckExitSteel(x, y);
11992     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11993       CheckExitSteelEM(x, y);
11994     else if (element == EL_SP_EXIT_CLOSED)
11995       CheckExitSP(x, y);
11996     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11997              element == EL_EXPANDABLE_STEELWALL_GROWING)
11998       MauerWaechst(x, y);
11999     else if (element == EL_EXPANDABLE_WALL ||
12000              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12001              element == EL_EXPANDABLE_WALL_VERTICAL ||
12002              element == EL_EXPANDABLE_WALL_ANY ||
12003              element == EL_BD_EXPANDABLE_WALL)
12004       MauerAbleger(x, y);
12005     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12006              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12007              element == EL_EXPANDABLE_STEELWALL_ANY)
12008       MauerAblegerStahl(x, y);
12009     else if (element == EL_FLAMES)
12010       CheckForDragon(x, y);
12011     else if (element == EL_EXPLOSION)
12012       ; // drawing of correct explosion animation is handled separately
12013     else if (element == EL_ELEMENT_SNAPPING ||
12014              element == EL_DIAGONAL_SHRINKING ||
12015              element == EL_DIAGONAL_GROWING)
12016     {
12017       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12018
12019       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12020     }
12021     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12022       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12023
12024     if (IS_BELT_ACTIVE(element))
12025       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12026
12027     if (game.magic_wall_active)
12028     {
12029       int jx = local_player->jx, jy = local_player->jy;
12030
12031       // play the element sound at the position nearest to the player
12032       if ((element == EL_MAGIC_WALL_FULL ||
12033            element == EL_MAGIC_WALL_ACTIVE ||
12034            element == EL_MAGIC_WALL_EMPTYING ||
12035            element == EL_BD_MAGIC_WALL_FULL ||
12036            element == EL_BD_MAGIC_WALL_ACTIVE ||
12037            element == EL_BD_MAGIC_WALL_EMPTYING ||
12038            element == EL_DC_MAGIC_WALL_FULL ||
12039            element == EL_DC_MAGIC_WALL_ACTIVE ||
12040            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12041           ABS(x - jx) + ABS(y - jy) <
12042           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12043       {
12044         magic_wall_x = x;
12045         magic_wall_y = y;
12046       }
12047     }
12048   }
12049
12050 #if USE_NEW_AMOEBA_CODE
12051   // new experimental amoeba growth stuff
12052   if (!(FrameCounter % 8))
12053   {
12054     static unsigned int random = 1684108901;
12055
12056     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12057     {
12058       x = RND(lev_fieldx);
12059       y = RND(lev_fieldy);
12060       element = Feld[x][y];
12061
12062       if (!IS_PLAYER(x,y) &&
12063           (element == EL_EMPTY ||
12064            CAN_GROW_INTO(element) ||
12065            element == EL_QUICKSAND_EMPTY ||
12066            element == EL_QUICKSAND_FAST_EMPTY ||
12067            element == EL_ACID_SPLASH_LEFT ||
12068            element == EL_ACID_SPLASH_RIGHT))
12069       {
12070         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12071             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12072             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12073             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12074           Feld[x][y] = EL_AMOEBA_DROP;
12075       }
12076
12077       random = random * 129 + 1;
12078     }
12079   }
12080 #endif
12081
12082   game.explosions_delayed = FALSE;
12083
12084   SCAN_PLAYFIELD(x, y)
12085   {
12086     element = Feld[x][y];
12087
12088     if (ExplodeField[x][y])
12089       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12090     else if (element == EL_EXPLOSION)
12091       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12092
12093     ExplodeField[x][y] = EX_TYPE_NONE;
12094   }
12095
12096   game.explosions_delayed = TRUE;
12097
12098   if (game.magic_wall_active)
12099   {
12100     if (!(game.magic_wall_time_left % 4))
12101     {
12102       int element = Feld[magic_wall_x][magic_wall_y];
12103
12104       if (element == EL_BD_MAGIC_WALL_FULL ||
12105           element == EL_BD_MAGIC_WALL_ACTIVE ||
12106           element == EL_BD_MAGIC_WALL_EMPTYING)
12107         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12108       else if (element == EL_DC_MAGIC_WALL_FULL ||
12109                element == EL_DC_MAGIC_WALL_ACTIVE ||
12110                element == EL_DC_MAGIC_WALL_EMPTYING)
12111         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12112       else
12113         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12114     }
12115
12116     if (game.magic_wall_time_left > 0)
12117     {
12118       game.magic_wall_time_left--;
12119
12120       if (!game.magic_wall_time_left)
12121       {
12122         SCAN_PLAYFIELD(x, y)
12123         {
12124           element = Feld[x][y];
12125
12126           if (element == EL_MAGIC_WALL_ACTIVE ||
12127               element == EL_MAGIC_WALL_FULL)
12128           {
12129             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12130             TEST_DrawLevelField(x, y);
12131           }
12132           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12133                    element == EL_BD_MAGIC_WALL_FULL)
12134           {
12135             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12136             TEST_DrawLevelField(x, y);
12137           }
12138           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12139                    element == EL_DC_MAGIC_WALL_FULL)
12140           {
12141             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12142             TEST_DrawLevelField(x, y);
12143           }
12144         }
12145
12146         game.magic_wall_active = FALSE;
12147       }
12148     }
12149   }
12150
12151   if (game.light_time_left > 0)
12152   {
12153     game.light_time_left--;
12154
12155     if (game.light_time_left == 0)
12156       RedrawAllLightSwitchesAndInvisibleElements();
12157   }
12158
12159   if (game.timegate_time_left > 0)
12160   {
12161     game.timegate_time_left--;
12162
12163     if (game.timegate_time_left == 0)
12164       CloseAllOpenTimegates();
12165   }
12166
12167   if (game.lenses_time_left > 0)
12168   {
12169     game.lenses_time_left--;
12170
12171     if (game.lenses_time_left == 0)
12172       RedrawAllInvisibleElementsForLenses();
12173   }
12174
12175   if (game.magnify_time_left > 0)
12176   {
12177     game.magnify_time_left--;
12178
12179     if (game.magnify_time_left == 0)
12180       RedrawAllInvisibleElementsForMagnifier();
12181   }
12182
12183   for (i = 0; i < MAX_PLAYERS; i++)
12184   {
12185     struct PlayerInfo *player = &stored_player[i];
12186
12187     if (SHIELD_ON(player))
12188     {
12189       if (player->shield_deadly_time_left)
12190         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12191       else if (player->shield_normal_time_left)
12192         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12193     }
12194   }
12195
12196 #if USE_DELAYED_GFX_REDRAW
12197   SCAN_PLAYFIELD(x, y)
12198   {
12199     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12200     {
12201       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12202          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12203
12204       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12205         DrawLevelField(x, y);
12206
12207       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12208         DrawLevelFieldCrumbled(x, y);
12209
12210       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12211         DrawLevelFieldCrumbledNeighbours(x, y);
12212
12213       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12214         DrawTwinkleOnField(x, y);
12215     }
12216
12217     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12218   }
12219 #endif
12220
12221   DrawAllPlayers();
12222   PlayAllPlayersSound();
12223
12224   for (i = 0; i < MAX_PLAYERS; i++)
12225   {
12226     struct PlayerInfo *player = &stored_player[i];
12227
12228     if (player->show_envelope != 0 && (!player->active ||
12229                                        player->MovPos == 0))
12230     {
12231       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12232
12233       player->show_envelope = 0;
12234     }
12235   }
12236
12237   // use random number generator in every frame to make it less predictable
12238   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12239     RND(1);
12240
12241   mouse_action_last = mouse_action;
12242 }
12243
12244 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12245 {
12246   int min_x = x, min_y = y, max_x = x, max_y = y;
12247   int i;
12248
12249   for (i = 0; i < MAX_PLAYERS; i++)
12250   {
12251     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12252
12253     if (!stored_player[i].active || &stored_player[i] == player)
12254       continue;
12255
12256     min_x = MIN(min_x, jx);
12257     min_y = MIN(min_y, jy);
12258     max_x = MAX(max_x, jx);
12259     max_y = MAX(max_y, jy);
12260   }
12261
12262   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12263 }
12264
12265 static boolean AllPlayersInVisibleScreen(void)
12266 {
12267   int i;
12268
12269   for (i = 0; i < MAX_PLAYERS; i++)
12270   {
12271     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12272
12273     if (!stored_player[i].active)
12274       continue;
12275
12276     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12277       return FALSE;
12278   }
12279
12280   return TRUE;
12281 }
12282
12283 void ScrollLevel(int dx, int dy)
12284 {
12285   int scroll_offset = 2 * TILEX_VAR;
12286   int x, y;
12287
12288   BlitBitmap(drawto_field, drawto_field,
12289              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12290              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12291              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12292              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12293              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12294              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12295
12296   if (dx != 0)
12297   {
12298     x = (dx == 1 ? BX1 : BX2);
12299     for (y = BY1; y <= BY2; y++)
12300       DrawScreenField(x, y);
12301   }
12302
12303   if (dy != 0)
12304   {
12305     y = (dy == 1 ? BY1 : BY2);
12306     for (x = BX1; x <= BX2; x++)
12307       DrawScreenField(x, y);
12308   }
12309
12310   redraw_mask |= REDRAW_FIELD;
12311 }
12312
12313 static boolean canFallDown(struct PlayerInfo *player)
12314 {
12315   int jx = player->jx, jy = player->jy;
12316
12317   return (IN_LEV_FIELD(jx, jy + 1) &&
12318           (IS_FREE(jx, jy + 1) ||
12319            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12320           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12321           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12322 }
12323
12324 static boolean canPassField(int x, int y, int move_dir)
12325 {
12326   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12327   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12328   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12329   int nextx = x + dx;
12330   int nexty = y + dy;
12331   int element = Feld[x][y];
12332
12333   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12334           !CAN_MOVE(element) &&
12335           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12336           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12337           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12338 }
12339
12340 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12341 {
12342   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12343   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12344   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12345   int newx = x + dx;
12346   int newy = y + dy;
12347
12348   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12349           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12350           (IS_DIGGABLE(Feld[newx][newy]) ||
12351            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12352            canPassField(newx, newy, move_dir)));
12353 }
12354
12355 static void CheckGravityMovement(struct PlayerInfo *player)
12356 {
12357   if (player->gravity && !player->programmed_action)
12358   {
12359     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12360     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12361     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12362     int jx = player->jx, jy = player->jy;
12363     boolean player_is_moving_to_valid_field =
12364       (!player_is_snapping &&
12365        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12366         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12367     boolean player_can_fall_down = canFallDown(player);
12368
12369     if (player_can_fall_down &&
12370         !player_is_moving_to_valid_field)
12371       player->programmed_action = MV_DOWN;
12372   }
12373 }
12374
12375 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12376 {
12377   return CheckGravityMovement(player);
12378
12379   if (player->gravity && !player->programmed_action)
12380   {
12381     int jx = player->jx, jy = player->jy;
12382     boolean field_under_player_is_free =
12383       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12384     boolean player_is_standing_on_valid_field =
12385       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12386        (IS_WALKABLE(Feld[jx][jy]) &&
12387         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12388
12389     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12390       player->programmed_action = MV_DOWN;
12391   }
12392 }
12393
12394 /*
12395   MovePlayerOneStep()
12396   -----------------------------------------------------------------------------
12397   dx, dy:               direction (non-diagonal) to try to move the player to
12398   real_dx, real_dy:     direction as read from input device (can be diagonal)
12399 */
12400
12401 boolean MovePlayerOneStep(struct PlayerInfo *player,
12402                           int dx, int dy, int real_dx, int real_dy)
12403 {
12404   int jx = player->jx, jy = player->jy;
12405   int new_jx = jx + dx, new_jy = jy + dy;
12406   int can_move;
12407   boolean player_can_move = !player->cannot_move;
12408
12409   if (!player->active || (!dx && !dy))
12410     return MP_NO_ACTION;
12411
12412   player->MovDir = (dx < 0 ? MV_LEFT :
12413                     dx > 0 ? MV_RIGHT :
12414                     dy < 0 ? MV_UP :
12415                     dy > 0 ? MV_DOWN :  MV_NONE);
12416
12417   if (!IN_LEV_FIELD(new_jx, new_jy))
12418     return MP_NO_ACTION;
12419
12420   if (!player_can_move)
12421   {
12422     if (player->MovPos == 0)
12423     {
12424       player->is_moving = FALSE;
12425       player->is_digging = FALSE;
12426       player->is_collecting = FALSE;
12427       player->is_snapping = FALSE;
12428       player->is_pushing = FALSE;
12429     }
12430   }
12431
12432   if (!network.enabled && game.centered_player_nr == -1 &&
12433       !AllPlayersInSight(player, new_jx, new_jy))
12434     return MP_NO_ACTION;
12435
12436   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12437   if (can_move != MP_MOVING)
12438     return can_move;
12439
12440   // check if DigField() has caused relocation of the player
12441   if (player->jx != jx || player->jy != jy)
12442     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12443
12444   StorePlayer[jx][jy] = 0;
12445   player->last_jx = jx;
12446   player->last_jy = jy;
12447   player->jx = new_jx;
12448   player->jy = new_jy;
12449   StorePlayer[new_jx][new_jy] = player->element_nr;
12450
12451   if (player->move_delay_value_next != -1)
12452   {
12453     player->move_delay_value = player->move_delay_value_next;
12454     player->move_delay_value_next = -1;
12455   }
12456
12457   player->MovPos =
12458     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12459
12460   player->step_counter++;
12461
12462   PlayerVisit[jx][jy] = FrameCounter;
12463
12464   player->is_moving = TRUE;
12465
12466 #if 1
12467   // should better be called in MovePlayer(), but this breaks some tapes
12468   ScrollPlayer(player, SCROLL_INIT);
12469 #endif
12470
12471   return MP_MOVING;
12472 }
12473
12474 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12475 {
12476   int jx = player->jx, jy = player->jy;
12477   int old_jx = jx, old_jy = jy;
12478   int moved = MP_NO_ACTION;
12479
12480   if (!player->active)
12481     return FALSE;
12482
12483   if (!dx && !dy)
12484   {
12485     if (player->MovPos == 0)
12486     {
12487       player->is_moving = FALSE;
12488       player->is_digging = FALSE;
12489       player->is_collecting = FALSE;
12490       player->is_snapping = FALSE;
12491       player->is_pushing = FALSE;
12492     }
12493
12494     return FALSE;
12495   }
12496
12497   if (player->move_delay > 0)
12498     return FALSE;
12499
12500   player->move_delay = -1;              // set to "uninitialized" value
12501
12502   // store if player is automatically moved to next field
12503   player->is_auto_moving = (player->programmed_action != MV_NONE);
12504
12505   // remove the last programmed player action
12506   player->programmed_action = 0;
12507
12508   if (player->MovPos)
12509   {
12510     // should only happen if pre-1.2 tape recordings are played
12511     // this is only for backward compatibility
12512
12513     int original_move_delay_value = player->move_delay_value;
12514
12515 #if DEBUG
12516     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12517            tape.counter);
12518 #endif
12519
12520     // scroll remaining steps with finest movement resolution
12521     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12522
12523     while (player->MovPos)
12524     {
12525       ScrollPlayer(player, SCROLL_GO_ON);
12526       ScrollScreen(NULL, SCROLL_GO_ON);
12527
12528       AdvanceFrameAndPlayerCounters(player->index_nr);
12529
12530       DrawAllPlayers();
12531       BackToFront_WithFrameDelay(0);
12532     }
12533
12534     player->move_delay_value = original_move_delay_value;
12535   }
12536
12537   player->is_active = FALSE;
12538
12539   if (player->last_move_dir & MV_HORIZONTAL)
12540   {
12541     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12542       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12543   }
12544   else
12545   {
12546     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12547       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12548   }
12549
12550   if (!moved && !player->is_active)
12551   {
12552     player->is_moving = FALSE;
12553     player->is_digging = FALSE;
12554     player->is_collecting = FALSE;
12555     player->is_snapping = FALSE;
12556     player->is_pushing = FALSE;
12557   }
12558
12559   jx = player->jx;
12560   jy = player->jy;
12561
12562   if (moved & MP_MOVING && !ScreenMovPos &&
12563       (player->index_nr == game.centered_player_nr ||
12564        game.centered_player_nr == -1))
12565   {
12566     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12567
12568     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12569     {
12570       // actual player has left the screen -- scroll in that direction
12571       if (jx != old_jx)         // player has moved horizontally
12572         scroll_x += (jx - old_jx);
12573       else                      // player has moved vertically
12574         scroll_y += (jy - old_jy);
12575     }
12576     else
12577     {
12578       int offset_raw = game.scroll_delay_value;
12579
12580       if (jx != old_jx)         // player has moved horizontally
12581       {
12582         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12583         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12584         int new_scroll_x = jx - MIDPOSX + offset_x;
12585
12586         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12587             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12588           scroll_x = new_scroll_x;
12589
12590         // don't scroll over playfield boundaries
12591         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12592
12593         // don't scroll more than one field at a time
12594         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12595
12596         // don't scroll against the player's moving direction
12597         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12598             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12599           scroll_x = old_scroll_x;
12600       }
12601       else                      // player has moved vertically
12602       {
12603         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12604         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12605         int new_scroll_y = jy - MIDPOSY + offset_y;
12606
12607         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12608             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12609           scroll_y = new_scroll_y;
12610
12611         // don't scroll over playfield boundaries
12612         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12613
12614         // don't scroll more than one field at a time
12615         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12616
12617         // don't scroll against the player's moving direction
12618         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12619             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12620           scroll_y = old_scroll_y;
12621       }
12622     }
12623
12624     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12625     {
12626       if (!network.enabled && game.centered_player_nr == -1 &&
12627           !AllPlayersInVisibleScreen())
12628       {
12629         scroll_x = old_scroll_x;
12630         scroll_y = old_scroll_y;
12631       }
12632       else
12633       {
12634         ScrollScreen(player, SCROLL_INIT);
12635         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12636       }
12637     }
12638   }
12639
12640   player->StepFrame = 0;
12641
12642   if (moved & MP_MOVING)
12643   {
12644     if (old_jx != jx && old_jy == jy)
12645       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12646     else if (old_jx == jx && old_jy != jy)
12647       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12648
12649     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12650
12651     player->last_move_dir = player->MovDir;
12652     player->is_moving = TRUE;
12653     player->is_snapping = FALSE;
12654     player->is_switching = FALSE;
12655     player->is_dropping = FALSE;
12656     player->is_dropping_pressed = FALSE;
12657     player->drop_pressed_delay = 0;
12658
12659 #if 0
12660     // should better be called here than above, but this breaks some tapes
12661     ScrollPlayer(player, SCROLL_INIT);
12662 #endif
12663   }
12664   else
12665   {
12666     CheckGravityMovementWhenNotMoving(player);
12667
12668     player->is_moving = FALSE;
12669
12670     /* at this point, the player is allowed to move, but cannot move right now
12671        (e.g. because of something blocking the way) -- ensure that the player
12672        is also allowed to move in the next frame (in old versions before 3.1.1,
12673        the player was forced to wait again for eight frames before next try) */
12674
12675     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12676       player->move_delay = 0;   // allow direct movement in the next frame
12677   }
12678
12679   if (player->move_delay == -1)         // not yet initialized by DigField()
12680     player->move_delay = player->move_delay_value;
12681
12682   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12683   {
12684     TestIfPlayerTouchesBadThing(jx, jy);
12685     TestIfPlayerTouchesCustomElement(jx, jy);
12686   }
12687
12688   if (!player->active)
12689     RemovePlayer(player);
12690
12691   return moved;
12692 }
12693
12694 void ScrollPlayer(struct PlayerInfo *player, int mode)
12695 {
12696   int jx = player->jx, jy = player->jy;
12697   int last_jx = player->last_jx, last_jy = player->last_jy;
12698   int move_stepsize = TILEX / player->move_delay_value;
12699
12700   if (!player->active)
12701     return;
12702
12703   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12704     return;
12705
12706   if (mode == SCROLL_INIT)
12707   {
12708     player->actual_frame_counter = FrameCounter;
12709     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12710
12711     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12712         Feld[last_jx][last_jy] == EL_EMPTY)
12713     {
12714       int last_field_block_delay = 0;   // start with no blocking at all
12715       int block_delay_adjustment = player->block_delay_adjustment;
12716
12717       // if player blocks last field, add delay for exactly one move
12718       if (player->block_last_field)
12719       {
12720         last_field_block_delay += player->move_delay_value;
12721
12722         // when blocking enabled, prevent moving up despite gravity
12723         if (player->gravity && player->MovDir == MV_UP)
12724           block_delay_adjustment = -1;
12725       }
12726
12727       // add block delay adjustment (also possible when not blocking)
12728       last_field_block_delay += block_delay_adjustment;
12729
12730       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12731       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12732     }
12733
12734     if (player->MovPos != 0)    // player has not yet reached destination
12735       return;
12736   }
12737   else if (!FrameReached(&player->actual_frame_counter, 1))
12738     return;
12739
12740   if (player->MovPos != 0)
12741   {
12742     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12743     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12744
12745     // before DrawPlayer() to draw correct player graphic for this case
12746     if (player->MovPos == 0)
12747       CheckGravityMovement(player);
12748   }
12749
12750   if (player->MovPos == 0)      // player reached destination field
12751   {
12752     if (player->move_delay_reset_counter > 0)
12753     {
12754       player->move_delay_reset_counter--;
12755
12756       if (player->move_delay_reset_counter == 0)
12757       {
12758         // continue with normal speed after quickly moving through gate
12759         HALVE_PLAYER_SPEED(player);
12760
12761         // be able to make the next move without delay
12762         player->move_delay = 0;
12763       }
12764     }
12765
12766     player->last_jx = jx;
12767     player->last_jy = jy;
12768
12769     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12770         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12771         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12772         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12773         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12774         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12775         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12776         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12777     {
12778       ExitPlayer(player);
12779
12780       if (game.players_still_needed == 0 &&
12781           (game.friends_still_needed == 0 ||
12782            IS_SP_ELEMENT(Feld[jx][jy])))
12783         LevelSolved();
12784     }
12785
12786     // this breaks one level: "machine", level 000
12787     {
12788       int move_direction = player->MovDir;
12789       int enter_side = MV_DIR_OPPOSITE(move_direction);
12790       int leave_side = move_direction;
12791       int old_jx = last_jx;
12792       int old_jy = last_jy;
12793       int old_element = Feld[old_jx][old_jy];
12794       int new_element = Feld[jx][jy];
12795
12796       if (IS_CUSTOM_ELEMENT(old_element))
12797         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12798                                    CE_LEFT_BY_PLAYER,
12799                                    player->index_bit, leave_side);
12800
12801       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12802                                           CE_PLAYER_LEAVES_X,
12803                                           player->index_bit, leave_side);
12804
12805       if (IS_CUSTOM_ELEMENT(new_element))
12806         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12807                                    player->index_bit, enter_side);
12808
12809       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12810                                           CE_PLAYER_ENTERS_X,
12811                                           player->index_bit, enter_side);
12812
12813       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12814                                         CE_MOVE_OF_X, move_direction);
12815     }
12816
12817     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12818     {
12819       TestIfPlayerTouchesBadThing(jx, jy);
12820       TestIfPlayerTouchesCustomElement(jx, jy);
12821
12822       /* needed because pushed element has not yet reached its destination,
12823          so it would trigger a change event at its previous field location */
12824       if (!player->is_pushing)
12825         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12826
12827       if (!player->active)
12828         RemovePlayer(player);
12829     }
12830
12831     if (!game.LevelSolved && level.use_step_counter)
12832     {
12833       int i;
12834
12835       TimePlayed++;
12836
12837       if (TimeLeft > 0)
12838       {
12839         TimeLeft--;
12840
12841         if (TimeLeft <= 10 && setup.time_limit)
12842           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12843
12844         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12845
12846         DisplayGameControlValues();
12847
12848         if (!TimeLeft && setup.time_limit)
12849           for (i = 0; i < MAX_PLAYERS; i++)
12850             KillPlayer(&stored_player[i]);
12851       }
12852       else if (game.no_time_limit && !game.all_players_gone)
12853       {
12854         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12855
12856         DisplayGameControlValues();
12857       }
12858     }
12859
12860     if (tape.single_step && tape.recording && !tape.pausing &&
12861         !player->programmed_action)
12862       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12863
12864     if (!player->programmed_action)
12865       CheckSaveEngineSnapshot(player);
12866   }
12867 }
12868
12869 void ScrollScreen(struct PlayerInfo *player, int mode)
12870 {
12871   static unsigned int screen_frame_counter = 0;
12872
12873   if (mode == SCROLL_INIT)
12874   {
12875     // set scrolling step size according to actual player's moving speed
12876     ScrollStepSize = TILEX / player->move_delay_value;
12877
12878     screen_frame_counter = FrameCounter;
12879     ScreenMovDir = player->MovDir;
12880     ScreenMovPos = player->MovPos;
12881     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12882     return;
12883   }
12884   else if (!FrameReached(&screen_frame_counter, 1))
12885     return;
12886
12887   if (ScreenMovPos)
12888   {
12889     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12890     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12891     redraw_mask |= REDRAW_FIELD;
12892   }
12893   else
12894     ScreenMovDir = MV_NONE;
12895 }
12896
12897 void TestIfPlayerTouchesCustomElement(int x, int y)
12898 {
12899   static int xy[4][2] =
12900   {
12901     { 0, -1 },
12902     { -1, 0 },
12903     { +1, 0 },
12904     { 0, +1 }
12905   };
12906   static int trigger_sides[4][2] =
12907   {
12908     // center side       border side
12909     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12910     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12911     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12912     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12913   };
12914   static int touch_dir[4] =
12915   {
12916     MV_LEFT | MV_RIGHT,
12917     MV_UP   | MV_DOWN,
12918     MV_UP   | MV_DOWN,
12919     MV_LEFT | MV_RIGHT
12920   };
12921   int center_element = Feld[x][y];      // should always be non-moving!
12922   int i;
12923
12924   for (i = 0; i < NUM_DIRECTIONS; i++)
12925   {
12926     int xx = x + xy[i][0];
12927     int yy = y + xy[i][1];
12928     int center_side = trigger_sides[i][0];
12929     int border_side = trigger_sides[i][1];
12930     int border_element;
12931
12932     if (!IN_LEV_FIELD(xx, yy))
12933       continue;
12934
12935     if (IS_PLAYER(x, y))                // player found at center element
12936     {
12937       struct PlayerInfo *player = PLAYERINFO(x, y);
12938
12939       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12940         border_element = Feld[xx][yy];          // may be moving!
12941       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12942         border_element = Feld[xx][yy];
12943       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12944         border_element = MovingOrBlocked2Element(xx, yy);
12945       else
12946         continue;               // center and border element do not touch
12947
12948       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12949                                  player->index_bit, border_side);
12950       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12951                                           CE_PLAYER_TOUCHES_X,
12952                                           player->index_bit, border_side);
12953
12954       {
12955         /* use player element that is initially defined in the level playfield,
12956            not the player element that corresponds to the runtime player number
12957            (example: a level that contains EL_PLAYER_3 as the only player would
12958            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12959         int player_element = PLAYERINFO(x, y)->initial_element;
12960
12961         CheckElementChangeBySide(xx, yy, border_element, player_element,
12962                                  CE_TOUCHING_X, border_side);
12963       }
12964     }
12965     else if (IS_PLAYER(xx, yy))         // player found at border element
12966     {
12967       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12968
12969       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12970       {
12971         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12972           continue;             // center and border element do not touch
12973       }
12974
12975       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12976                                  player->index_bit, center_side);
12977       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12978                                           CE_PLAYER_TOUCHES_X,
12979                                           player->index_bit, center_side);
12980
12981       {
12982         /* use player element that is initially defined in the level playfield,
12983            not the player element that corresponds to the runtime player number
12984            (example: a level that contains EL_PLAYER_3 as the only player would
12985            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12986         int player_element = PLAYERINFO(xx, yy)->initial_element;
12987
12988         CheckElementChangeBySide(x, y, center_element, player_element,
12989                                  CE_TOUCHING_X, center_side);
12990       }
12991
12992       break;
12993     }
12994   }
12995 }
12996
12997 void TestIfElementTouchesCustomElement(int x, int y)
12998 {
12999   static int xy[4][2] =
13000   {
13001     { 0, -1 },
13002     { -1, 0 },
13003     { +1, 0 },
13004     { 0, +1 }
13005   };
13006   static int trigger_sides[4][2] =
13007   {
13008     // center side      border side
13009     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13010     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13011     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13012     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13013   };
13014   static int touch_dir[4] =
13015   {
13016     MV_LEFT | MV_RIGHT,
13017     MV_UP   | MV_DOWN,
13018     MV_UP   | MV_DOWN,
13019     MV_LEFT | MV_RIGHT
13020   };
13021   boolean change_center_element = FALSE;
13022   int center_element = Feld[x][y];      // should always be non-moving!
13023   int border_element_old[NUM_DIRECTIONS];
13024   int i;
13025
13026   for (i = 0; i < NUM_DIRECTIONS; i++)
13027   {
13028     int xx = x + xy[i][0];
13029     int yy = y + xy[i][1];
13030     int border_element;
13031
13032     border_element_old[i] = -1;
13033
13034     if (!IN_LEV_FIELD(xx, yy))
13035       continue;
13036
13037     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13038       border_element = Feld[xx][yy];    // may be moving!
13039     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13040       border_element = Feld[xx][yy];
13041     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13042       border_element = MovingOrBlocked2Element(xx, yy);
13043     else
13044       continue;                 // center and border element do not touch
13045
13046     border_element_old[i] = border_element;
13047   }
13048
13049   for (i = 0; i < NUM_DIRECTIONS; i++)
13050   {
13051     int xx = x + xy[i][0];
13052     int yy = y + xy[i][1];
13053     int center_side = trigger_sides[i][0];
13054     int border_element = border_element_old[i];
13055
13056     if (border_element == -1)
13057       continue;
13058
13059     // check for change of border element
13060     CheckElementChangeBySide(xx, yy, border_element, center_element,
13061                              CE_TOUCHING_X, center_side);
13062
13063     // (center element cannot be player, so we dont have to check this here)
13064   }
13065
13066   for (i = 0; i < NUM_DIRECTIONS; i++)
13067   {
13068     int xx = x + xy[i][0];
13069     int yy = y + xy[i][1];
13070     int border_side = trigger_sides[i][1];
13071     int border_element = border_element_old[i];
13072
13073     if (border_element == -1)
13074       continue;
13075
13076     // check for change of center element (but change it only once)
13077     if (!change_center_element)
13078       change_center_element =
13079         CheckElementChangeBySide(x, y, center_element, border_element,
13080                                  CE_TOUCHING_X, border_side);
13081
13082     if (IS_PLAYER(xx, yy))
13083     {
13084       /* use player element that is initially defined in the level playfield,
13085          not the player element that corresponds to the runtime player number
13086          (example: a level that contains EL_PLAYER_3 as the only player would
13087          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13088       int player_element = PLAYERINFO(xx, yy)->initial_element;
13089
13090       CheckElementChangeBySide(x, y, center_element, player_element,
13091                                CE_TOUCHING_X, border_side);
13092     }
13093   }
13094 }
13095
13096 void TestIfElementHitsCustomElement(int x, int y, int direction)
13097 {
13098   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13099   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13100   int hitx = x + dx, hity = y + dy;
13101   int hitting_element = Feld[x][y];
13102   int touched_element;
13103
13104   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13105     return;
13106
13107   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13108                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13109
13110   if (IN_LEV_FIELD(hitx, hity))
13111   {
13112     int opposite_direction = MV_DIR_OPPOSITE(direction);
13113     int hitting_side = direction;
13114     int touched_side = opposite_direction;
13115     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13116                           MovDir[hitx][hity] != direction ||
13117                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13118
13119     object_hit = TRUE;
13120
13121     if (object_hit)
13122     {
13123       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13124                                CE_HITTING_X, touched_side);
13125
13126       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13127                                CE_HIT_BY_X, hitting_side);
13128
13129       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13130                                CE_HIT_BY_SOMETHING, opposite_direction);
13131
13132       if (IS_PLAYER(hitx, hity))
13133       {
13134         /* use player element that is initially defined in the level playfield,
13135            not the player element that corresponds to the runtime player number
13136            (example: a level that contains EL_PLAYER_3 as the only player would
13137            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13138         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13139
13140         CheckElementChangeBySide(x, y, hitting_element, player_element,
13141                                  CE_HITTING_X, touched_side);
13142       }
13143     }
13144   }
13145
13146   // "hitting something" is also true when hitting the playfield border
13147   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13148                            CE_HITTING_SOMETHING, direction);
13149 }
13150
13151 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13152 {
13153   int i, kill_x = -1, kill_y = -1;
13154
13155   int bad_element = -1;
13156   static int test_xy[4][2] =
13157   {
13158     { 0, -1 },
13159     { -1, 0 },
13160     { +1, 0 },
13161     { 0, +1 }
13162   };
13163   static int test_dir[4] =
13164   {
13165     MV_UP,
13166     MV_LEFT,
13167     MV_RIGHT,
13168     MV_DOWN
13169   };
13170
13171   for (i = 0; i < NUM_DIRECTIONS; i++)
13172   {
13173     int test_x, test_y, test_move_dir, test_element;
13174
13175     test_x = good_x + test_xy[i][0];
13176     test_y = good_y + test_xy[i][1];
13177
13178     if (!IN_LEV_FIELD(test_x, test_y))
13179       continue;
13180
13181     test_move_dir =
13182       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13183
13184     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13185
13186     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13187        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13188     */
13189     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13190         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13191     {
13192       kill_x = test_x;
13193       kill_y = test_y;
13194       bad_element = test_element;
13195
13196       break;
13197     }
13198   }
13199
13200   if (kill_x != -1 || kill_y != -1)
13201   {
13202     if (IS_PLAYER(good_x, good_y))
13203     {
13204       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13205
13206       if (player->shield_deadly_time_left > 0 &&
13207           !IS_INDESTRUCTIBLE(bad_element))
13208         Bang(kill_x, kill_y);
13209       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13210         KillPlayer(player);
13211     }
13212     else
13213       Bang(good_x, good_y);
13214   }
13215 }
13216
13217 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13218 {
13219   int i, kill_x = -1, kill_y = -1;
13220   int bad_element = Feld[bad_x][bad_y];
13221   static int test_xy[4][2] =
13222   {
13223     { 0, -1 },
13224     { -1, 0 },
13225     { +1, 0 },
13226     { 0, +1 }
13227   };
13228   static int touch_dir[4] =
13229   {
13230     MV_LEFT | MV_RIGHT,
13231     MV_UP   | MV_DOWN,
13232     MV_UP   | MV_DOWN,
13233     MV_LEFT | MV_RIGHT
13234   };
13235   static int test_dir[4] =
13236   {
13237     MV_UP,
13238     MV_LEFT,
13239     MV_RIGHT,
13240     MV_DOWN
13241   };
13242
13243   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13244     return;
13245
13246   for (i = 0; i < NUM_DIRECTIONS; i++)
13247   {
13248     int test_x, test_y, test_move_dir, test_element;
13249
13250     test_x = bad_x + test_xy[i][0];
13251     test_y = bad_y + test_xy[i][1];
13252
13253     if (!IN_LEV_FIELD(test_x, test_y))
13254       continue;
13255
13256     test_move_dir =
13257       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13258
13259     test_element = Feld[test_x][test_y];
13260
13261     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13262        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13263     */
13264     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13265         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13266     {
13267       // good thing is player or penguin that does not move away
13268       if (IS_PLAYER(test_x, test_y))
13269       {
13270         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13271
13272         if (bad_element == EL_ROBOT && player->is_moving)
13273           continue;     // robot does not kill player if he is moving
13274
13275         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13276         {
13277           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13278             continue;           // center and border element do not touch
13279         }
13280
13281         kill_x = test_x;
13282         kill_y = test_y;
13283
13284         break;
13285       }
13286       else if (test_element == EL_PENGUIN)
13287       {
13288         kill_x = test_x;
13289         kill_y = test_y;
13290
13291         break;
13292       }
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 TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13314 {
13315   int bad_element = Feld[bad_x][bad_y];
13316   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13317   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13318   int test_x = bad_x + dx, test_y = bad_y + dy;
13319   int test_move_dir, test_element;
13320   int kill_x = -1, kill_y = -1;
13321
13322   if (!IN_LEV_FIELD(test_x, test_y))
13323     return;
13324
13325   test_move_dir =
13326     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13327
13328   test_element = Feld[test_x][test_y];
13329
13330   if (test_move_dir != bad_move_dir)
13331   {
13332     // good thing can be player or penguin that does not move away
13333     if (IS_PLAYER(test_x, test_y))
13334     {
13335       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13336
13337       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13338          player as being hit when he is moving towards the bad thing, because
13339          the "get hit by" condition would be lost after the player stops) */
13340       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13341         return;         // player moves away from bad thing
13342
13343       kill_x = test_x;
13344       kill_y = test_y;
13345     }
13346     else if (test_element == EL_PENGUIN)
13347     {
13348       kill_x = test_x;
13349       kill_y = test_y;
13350     }
13351   }
13352
13353   if (kill_x != -1 || kill_y != -1)
13354   {
13355     if (IS_PLAYER(kill_x, kill_y))
13356     {
13357       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13358
13359       if (player->shield_deadly_time_left > 0 &&
13360           !IS_INDESTRUCTIBLE(bad_element))
13361         Bang(bad_x, bad_y);
13362       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13363         KillPlayer(player);
13364     }
13365     else
13366       Bang(kill_x, kill_y);
13367   }
13368 }
13369
13370 void TestIfPlayerTouchesBadThing(int x, int y)
13371 {
13372   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13373 }
13374
13375 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13376 {
13377   TestIfGoodThingHitsBadThing(x, y, move_dir);
13378 }
13379
13380 void TestIfBadThingTouchesPlayer(int x, int y)
13381 {
13382   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13383 }
13384
13385 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13386 {
13387   TestIfBadThingHitsGoodThing(x, y, move_dir);
13388 }
13389
13390 void TestIfFriendTouchesBadThing(int x, int y)
13391 {
13392   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13393 }
13394
13395 void TestIfBadThingTouchesFriend(int x, int y)
13396 {
13397   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13398 }
13399
13400 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13401 {
13402   int i, kill_x = bad_x, kill_y = bad_y;
13403   static int xy[4][2] =
13404   {
13405     { 0, -1 },
13406     { -1, 0 },
13407     { +1, 0 },
13408     { 0, +1 }
13409   };
13410
13411   for (i = 0; i < NUM_DIRECTIONS; i++)
13412   {
13413     int x, y, element;
13414
13415     x = bad_x + xy[i][0];
13416     y = bad_y + xy[i][1];
13417     if (!IN_LEV_FIELD(x, y))
13418       continue;
13419
13420     element = Feld[x][y];
13421     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13422         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13423     {
13424       kill_x = x;
13425       kill_y = y;
13426       break;
13427     }
13428   }
13429
13430   if (kill_x != bad_x || kill_y != bad_y)
13431     Bang(bad_x, bad_y);
13432 }
13433
13434 void KillPlayer(struct PlayerInfo *player)
13435 {
13436   int jx = player->jx, jy = player->jy;
13437
13438   if (!player->active)
13439     return;
13440
13441 #if 0
13442   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13443          player->killed, player->active, player->reanimated);
13444 #endif
13445
13446   /* the following code was introduced to prevent an infinite loop when calling
13447      -> Bang()
13448      -> CheckTriggeredElementChangeExt()
13449      -> ExecuteCustomElementAction()
13450      -> KillPlayer()
13451      -> (infinitely repeating the above sequence of function calls)
13452      which occurs when killing the player while having a CE with the setting
13453      "kill player X when explosion of <player X>"; the solution using a new
13454      field "player->killed" was chosen for backwards compatibility, although
13455      clever use of the fields "player->active" etc. would probably also work */
13456 #if 1
13457   if (player->killed)
13458     return;
13459 #endif
13460
13461   player->killed = TRUE;
13462
13463   // remove accessible field at the player's position
13464   Feld[jx][jy] = EL_EMPTY;
13465
13466   // deactivate shield (else Bang()/Explode() would not work right)
13467   player->shield_normal_time_left = 0;
13468   player->shield_deadly_time_left = 0;
13469
13470 #if 0
13471   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13472          player->killed, player->active, player->reanimated);
13473 #endif
13474
13475   Bang(jx, jy);
13476
13477 #if 0
13478   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13479          player->killed, player->active, player->reanimated);
13480 #endif
13481
13482   if (player->reanimated)       // killed player may have been reanimated
13483     player->killed = player->reanimated = FALSE;
13484   else
13485     BuryPlayer(player);
13486 }
13487
13488 static void KillPlayerUnlessEnemyProtected(int x, int y)
13489 {
13490   if (!PLAYER_ENEMY_PROTECTED(x, y))
13491     KillPlayer(PLAYERINFO(x, y));
13492 }
13493
13494 static void KillPlayerUnlessExplosionProtected(int x, int y)
13495 {
13496   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13497     KillPlayer(PLAYERINFO(x, y));
13498 }
13499
13500 void BuryPlayer(struct PlayerInfo *player)
13501 {
13502   int jx = player->jx, jy = player->jy;
13503
13504   if (!player->active)
13505     return;
13506
13507   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13508   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13509
13510   RemovePlayer(player);
13511
13512   player->buried = TRUE;
13513
13514   if (game.all_players_gone)
13515     game.GameOver = TRUE;
13516 }
13517
13518 void RemovePlayer(struct PlayerInfo *player)
13519 {
13520   int jx = player->jx, jy = player->jy;
13521   int i, found = FALSE;
13522
13523   player->present = FALSE;
13524   player->active = FALSE;
13525
13526   // required for some CE actions (even if the player is not active anymore)
13527   player->MovPos = 0;
13528
13529   if (!ExplodeField[jx][jy])
13530     StorePlayer[jx][jy] = 0;
13531
13532   if (player->is_moving)
13533     TEST_DrawLevelField(player->last_jx, player->last_jy);
13534
13535   for (i = 0; i < MAX_PLAYERS; i++)
13536     if (stored_player[i].active)
13537       found = TRUE;
13538
13539   if (!found)
13540   {
13541     game.all_players_gone = TRUE;
13542     game.GameOver = TRUE;
13543   }
13544
13545   game.exit_x = game.robot_wheel_x = jx;
13546   game.exit_y = game.robot_wheel_y = jy;
13547 }
13548
13549 void ExitPlayer(struct PlayerInfo *player)
13550 {
13551   DrawPlayer(player);   // needed here only to cleanup last field
13552   RemovePlayer(player);
13553
13554   if (game.players_still_needed > 0)
13555     game.players_still_needed--;
13556 }
13557
13558 static void setFieldForSnapping(int x, int y, int element, int direction)
13559 {
13560   struct ElementInfo *ei = &element_info[element];
13561   int direction_bit = MV_DIR_TO_BIT(direction);
13562   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13563   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13564                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13565
13566   Feld[x][y] = EL_ELEMENT_SNAPPING;
13567   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13568
13569   ResetGfxAnimation(x, y);
13570
13571   GfxElement[x][y] = element;
13572   GfxAction[x][y] = action;
13573   GfxDir[x][y] = direction;
13574   GfxFrame[x][y] = -1;
13575 }
13576
13577 /*
13578   =============================================================================
13579   checkDiagonalPushing()
13580   -----------------------------------------------------------------------------
13581   check if diagonal input device direction results in pushing of object
13582   (by checking if the alternative direction is walkable, diggable, ...)
13583   =============================================================================
13584 */
13585
13586 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13587                                     int x, int y, int real_dx, int real_dy)
13588 {
13589   int jx, jy, dx, dy, xx, yy;
13590
13591   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13592     return TRUE;
13593
13594   // diagonal direction: check alternative direction
13595   jx = player->jx;
13596   jy = player->jy;
13597   dx = x - jx;
13598   dy = y - jy;
13599   xx = jx + (dx == 0 ? real_dx : 0);
13600   yy = jy + (dy == 0 ? real_dy : 0);
13601
13602   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13603 }
13604
13605 /*
13606   =============================================================================
13607   DigField()
13608   -----------------------------------------------------------------------------
13609   x, y:                 field next to player (non-diagonal) to try to dig to
13610   real_dx, real_dy:     direction as read from input device (can be diagonal)
13611   =============================================================================
13612 */
13613
13614 static int DigField(struct PlayerInfo *player,
13615                     int oldx, int oldy, int x, int y,
13616                     int real_dx, int real_dy, int mode)
13617 {
13618   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13619   boolean player_was_pushing = player->is_pushing;
13620   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13621   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13622   int jx = oldx, jy = oldy;
13623   int dx = x - jx, dy = y - jy;
13624   int nextx = x + dx, nexty = y + dy;
13625   int move_direction = (dx == -1 ? MV_LEFT  :
13626                         dx == +1 ? MV_RIGHT :
13627                         dy == -1 ? MV_UP    :
13628                         dy == +1 ? MV_DOWN  : MV_NONE);
13629   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13630   int dig_side = MV_DIR_OPPOSITE(move_direction);
13631   int old_element = Feld[jx][jy];
13632   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13633   int collect_count;
13634
13635   if (is_player)                // function can also be called by EL_PENGUIN
13636   {
13637     if (player->MovPos == 0)
13638     {
13639       player->is_digging = FALSE;
13640       player->is_collecting = FALSE;
13641     }
13642
13643     if (player->MovPos == 0)    // last pushing move finished
13644       player->is_pushing = FALSE;
13645
13646     if (mode == DF_NO_PUSH)     // player just stopped pushing
13647     {
13648       player->is_switching = FALSE;
13649       player->push_delay = -1;
13650
13651       return MP_NO_ACTION;
13652     }
13653   }
13654
13655   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13656     old_element = Back[jx][jy];
13657
13658   // in case of element dropped at player position, check background
13659   else if (Back[jx][jy] != EL_EMPTY &&
13660            game.engine_version >= VERSION_IDENT(2,2,0,0))
13661     old_element = Back[jx][jy];
13662
13663   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13664     return MP_NO_ACTION;        // field has no opening in this direction
13665
13666   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13667     return MP_NO_ACTION;        // field has no opening in this direction
13668
13669   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13670   {
13671     SplashAcid(x, y);
13672
13673     Feld[jx][jy] = player->artwork_element;
13674     InitMovingField(jx, jy, MV_DOWN);
13675     Store[jx][jy] = EL_ACID;
13676     ContinueMoving(jx, jy);
13677     BuryPlayer(player);
13678
13679     return MP_DONT_RUN_INTO;
13680   }
13681
13682   if (player_can_move && DONT_RUN_INTO(element))
13683   {
13684     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13685
13686     return MP_DONT_RUN_INTO;
13687   }
13688
13689   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13690     return MP_NO_ACTION;
13691
13692   collect_count = element_info[element].collect_count_initial;
13693
13694   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13695     return MP_NO_ACTION;
13696
13697   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13698     player_can_move = player_can_move_or_snap;
13699
13700   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13701       game.engine_version >= VERSION_IDENT(2,2,0,0))
13702   {
13703     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13704                                player->index_bit, dig_side);
13705     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13706                                         player->index_bit, dig_side);
13707
13708     if (element == EL_DC_LANDMINE)
13709       Bang(x, y);
13710
13711     if (Feld[x][y] != element)          // field changed by snapping
13712       return MP_ACTION;
13713
13714     return MP_NO_ACTION;
13715   }
13716
13717   if (player->gravity && is_player && !player->is_auto_moving &&
13718       canFallDown(player) && move_direction != MV_DOWN &&
13719       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13720     return MP_NO_ACTION;        // player cannot walk here due to gravity
13721
13722   if (player_can_move &&
13723       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13724   {
13725     int sound_element = SND_ELEMENT(element);
13726     int sound_action = ACTION_WALKING;
13727
13728     if (IS_RND_GATE(element))
13729     {
13730       if (!player->key[RND_GATE_NR(element)])
13731         return MP_NO_ACTION;
13732     }
13733     else if (IS_RND_GATE_GRAY(element))
13734     {
13735       if (!player->key[RND_GATE_GRAY_NR(element)])
13736         return MP_NO_ACTION;
13737     }
13738     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13739     {
13740       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13741         return MP_NO_ACTION;
13742     }
13743     else if (element == EL_EXIT_OPEN ||
13744              element == EL_EM_EXIT_OPEN ||
13745              element == EL_EM_EXIT_OPENING ||
13746              element == EL_STEEL_EXIT_OPEN ||
13747              element == EL_EM_STEEL_EXIT_OPEN ||
13748              element == EL_EM_STEEL_EXIT_OPENING ||
13749              element == EL_SP_EXIT_OPEN ||
13750              element == EL_SP_EXIT_OPENING)
13751     {
13752       sound_action = ACTION_PASSING;    // player is passing exit
13753     }
13754     else if (element == EL_EMPTY)
13755     {
13756       sound_action = ACTION_MOVING;             // nothing to walk on
13757     }
13758
13759     // play sound from background or player, whatever is available
13760     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13761       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13762     else
13763       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13764   }
13765   else if (player_can_move &&
13766            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13767   {
13768     if (!ACCESS_FROM(element, opposite_direction))
13769       return MP_NO_ACTION;      // field not accessible from this direction
13770
13771     if (CAN_MOVE(element))      // only fixed elements can be passed!
13772       return MP_NO_ACTION;
13773
13774     if (IS_EM_GATE(element))
13775     {
13776       if (!player->key[EM_GATE_NR(element)])
13777         return MP_NO_ACTION;
13778     }
13779     else if (IS_EM_GATE_GRAY(element))
13780     {
13781       if (!player->key[EM_GATE_GRAY_NR(element)])
13782         return MP_NO_ACTION;
13783     }
13784     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13785     {
13786       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13787         return MP_NO_ACTION;
13788     }
13789     else if (IS_EMC_GATE(element))
13790     {
13791       if (!player->key[EMC_GATE_NR(element)])
13792         return MP_NO_ACTION;
13793     }
13794     else if (IS_EMC_GATE_GRAY(element))
13795     {
13796       if (!player->key[EMC_GATE_GRAY_NR(element)])
13797         return MP_NO_ACTION;
13798     }
13799     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13800     {
13801       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13802         return MP_NO_ACTION;
13803     }
13804     else if (element == EL_DC_GATE_WHITE ||
13805              element == EL_DC_GATE_WHITE_GRAY ||
13806              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13807     {
13808       if (player->num_white_keys == 0)
13809         return MP_NO_ACTION;
13810
13811       player->num_white_keys--;
13812     }
13813     else if (IS_SP_PORT(element))
13814     {
13815       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13816           element == EL_SP_GRAVITY_PORT_RIGHT ||
13817           element == EL_SP_GRAVITY_PORT_UP ||
13818           element == EL_SP_GRAVITY_PORT_DOWN)
13819         player->gravity = !player->gravity;
13820       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13821                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13822                element == EL_SP_GRAVITY_ON_PORT_UP ||
13823                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13824         player->gravity = TRUE;
13825       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13826                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13827                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13828                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13829         player->gravity = FALSE;
13830     }
13831
13832     // automatically move to the next field with double speed
13833     player->programmed_action = move_direction;
13834
13835     if (player->move_delay_reset_counter == 0)
13836     {
13837       player->move_delay_reset_counter = 2;     // two double speed steps
13838
13839       DOUBLE_PLAYER_SPEED(player);
13840     }
13841
13842     PlayLevelSoundAction(x, y, ACTION_PASSING);
13843   }
13844   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13845   {
13846     RemoveField(x, y);
13847
13848     if (mode != DF_SNAP)
13849     {
13850       GfxElement[x][y] = GFX_ELEMENT(element);
13851       player->is_digging = TRUE;
13852     }
13853
13854     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13855
13856     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13857                                         player->index_bit, dig_side);
13858
13859     if (mode == DF_SNAP)
13860     {
13861       if (level.block_snap_field)
13862         setFieldForSnapping(x, y, element, move_direction);
13863       else
13864         TestIfElementTouchesCustomElement(x, y);        // for empty space
13865
13866       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13867                                           player->index_bit, dig_side);
13868     }
13869   }
13870   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13871   {
13872     RemoveField(x, y);
13873
13874     if (is_player && mode != DF_SNAP)
13875     {
13876       GfxElement[x][y] = element;
13877       player->is_collecting = TRUE;
13878     }
13879
13880     if (element == EL_SPEED_PILL)
13881     {
13882       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13883     }
13884     else if (element == EL_EXTRA_TIME && level.time > 0)
13885     {
13886       TimeLeft += level.extra_time;
13887
13888       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13889
13890       DisplayGameControlValues();
13891     }
13892     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13893     {
13894       player->shield_normal_time_left += level.shield_normal_time;
13895       if (element == EL_SHIELD_DEADLY)
13896         player->shield_deadly_time_left += level.shield_deadly_time;
13897     }
13898     else if (element == EL_DYNAMITE ||
13899              element == EL_EM_DYNAMITE ||
13900              element == EL_SP_DISK_RED)
13901     {
13902       if (player->inventory_size < MAX_INVENTORY_SIZE)
13903         player->inventory_element[player->inventory_size++] = element;
13904
13905       DrawGameDoorValues();
13906     }
13907     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13908     {
13909       player->dynabomb_count++;
13910       player->dynabombs_left++;
13911     }
13912     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13913     {
13914       player->dynabomb_size++;
13915     }
13916     else if (element == EL_DYNABOMB_INCREASE_POWER)
13917     {
13918       player->dynabomb_xl = TRUE;
13919     }
13920     else if (IS_KEY(element))
13921     {
13922       player->key[KEY_NR(element)] = TRUE;
13923
13924       DrawGameDoorValues();
13925     }
13926     else if (element == EL_DC_KEY_WHITE)
13927     {
13928       player->num_white_keys++;
13929
13930       // display white keys?
13931       // DrawGameDoorValues();
13932     }
13933     else if (IS_ENVELOPE(element))
13934     {
13935       player->show_envelope = element;
13936     }
13937     else if (element == EL_EMC_LENSES)
13938     {
13939       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13940
13941       RedrawAllInvisibleElementsForLenses();
13942     }
13943     else if (element == EL_EMC_MAGNIFIER)
13944     {
13945       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13946
13947       RedrawAllInvisibleElementsForMagnifier();
13948     }
13949     else if (IS_DROPPABLE(element) ||
13950              IS_THROWABLE(element))     // can be collected and dropped
13951     {
13952       int i;
13953
13954       if (collect_count == 0)
13955         player->inventory_infinite_element = element;
13956       else
13957         for (i = 0; i < collect_count; i++)
13958           if (player->inventory_size < MAX_INVENTORY_SIZE)
13959             player->inventory_element[player->inventory_size++] = element;
13960
13961       DrawGameDoorValues();
13962     }
13963     else if (collect_count > 0)
13964     {
13965       game.gems_still_needed -= collect_count;
13966       if (game.gems_still_needed < 0)
13967         game.gems_still_needed = 0;
13968
13969       game.snapshot.collected_item = TRUE;
13970
13971       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13972
13973       DisplayGameControlValues();
13974     }
13975
13976     RaiseScoreElement(element);
13977     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13978
13979     if (is_player)
13980       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13981                                           player->index_bit, dig_side);
13982
13983     if (mode == DF_SNAP)
13984     {
13985       if (level.block_snap_field)
13986         setFieldForSnapping(x, y, element, move_direction);
13987       else
13988         TestIfElementTouchesCustomElement(x, y);        // for empty space
13989
13990       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13991                                           player->index_bit, dig_side);
13992     }
13993   }
13994   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13995   {
13996     if (mode == DF_SNAP && element != EL_BD_ROCK)
13997       return MP_NO_ACTION;
13998
13999     if (CAN_FALL(element) && dy)
14000       return MP_NO_ACTION;
14001
14002     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14003         !(element == EL_SPRING && level.use_spring_bug))
14004       return MP_NO_ACTION;
14005
14006     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14007         ((move_direction & MV_VERTICAL &&
14008           ((element_info[element].move_pattern & MV_LEFT &&
14009             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14010            (element_info[element].move_pattern & MV_RIGHT &&
14011             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14012          (move_direction & MV_HORIZONTAL &&
14013           ((element_info[element].move_pattern & MV_UP &&
14014             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14015            (element_info[element].move_pattern & MV_DOWN &&
14016             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14017       return MP_NO_ACTION;
14018
14019     // do not push elements already moving away faster than player
14020     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14021         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14022       return MP_NO_ACTION;
14023
14024     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14025     {
14026       if (player->push_delay_value == -1 || !player_was_pushing)
14027         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14028     }
14029     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14030     {
14031       if (player->push_delay_value == -1)
14032         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14033     }
14034     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14035     {
14036       if (!player->is_pushing)
14037         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14038     }
14039
14040     player->is_pushing = TRUE;
14041     player->is_active = TRUE;
14042
14043     if (!(IN_LEV_FIELD(nextx, nexty) &&
14044           (IS_FREE(nextx, nexty) ||
14045            (IS_SB_ELEMENT(element) &&
14046             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14047            (IS_CUSTOM_ELEMENT(element) &&
14048             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14049       return MP_NO_ACTION;
14050
14051     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14052       return MP_NO_ACTION;
14053
14054     if (player->push_delay == -1)       // new pushing; restart delay
14055       player->push_delay = 0;
14056
14057     if (player->push_delay < player->push_delay_value &&
14058         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14059         element != EL_SPRING && element != EL_BALLOON)
14060     {
14061       // make sure that there is no move delay before next try to push
14062       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14063         player->move_delay = 0;
14064
14065       return MP_NO_ACTION;
14066     }
14067
14068     if (IS_CUSTOM_ELEMENT(element) &&
14069         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14070     {
14071       if (!DigFieldByCE(nextx, nexty, element))
14072         return MP_NO_ACTION;
14073     }
14074
14075     if (IS_SB_ELEMENT(element))
14076     {
14077       boolean sokoban_task_solved = FALSE;
14078
14079       if (element == EL_SOKOBAN_FIELD_FULL)
14080       {
14081         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14082
14083         IncrementSokobanFieldsNeeded();
14084         IncrementSokobanObjectsNeeded();
14085       }
14086
14087       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14088       {
14089         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14090
14091         DecrementSokobanFieldsNeeded();
14092         DecrementSokobanObjectsNeeded();
14093
14094         // sokoban object was pushed from empty field to sokoban field
14095         if (Back[x][y] == EL_EMPTY)
14096           sokoban_task_solved = TRUE;
14097       }
14098
14099       Feld[x][y] = EL_SOKOBAN_OBJECT;
14100
14101       if (Back[x][y] == Back[nextx][nexty])
14102         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14103       else if (Back[x][y] != 0)
14104         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14105                                     ACTION_EMPTYING);
14106       else
14107         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14108                                     ACTION_FILLING);
14109
14110       if (sokoban_task_solved &&
14111           game.sokoban_fields_still_needed == 0 &&
14112           game.sokoban_objects_still_needed == 0 &&
14113           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14114       {
14115         game.players_still_needed = 0;
14116
14117         LevelSolved();
14118
14119         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14120       }
14121     }
14122     else
14123       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14124
14125     InitMovingField(x, y, move_direction);
14126     GfxAction[x][y] = ACTION_PUSHING;
14127
14128     if (mode == DF_SNAP)
14129       ContinueMoving(x, y);
14130     else
14131       MovPos[x][y] = (dx != 0 ? dx : dy);
14132
14133     Pushed[x][y] = TRUE;
14134     Pushed[nextx][nexty] = TRUE;
14135
14136     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14137       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14138     else
14139       player->push_delay_value = -1;    // get new value later
14140
14141     // check for element change _after_ element has been pushed
14142     if (game.use_change_when_pushing_bug)
14143     {
14144       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14145                                  player->index_bit, dig_side);
14146       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14147                                           player->index_bit, dig_side);
14148     }
14149   }
14150   else if (IS_SWITCHABLE(element))
14151   {
14152     if (PLAYER_SWITCHING(player, x, y))
14153     {
14154       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14155                                           player->index_bit, dig_side);
14156
14157       return MP_ACTION;
14158     }
14159
14160     player->is_switching = TRUE;
14161     player->switch_x = x;
14162     player->switch_y = y;
14163
14164     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14165
14166     if (element == EL_ROBOT_WHEEL)
14167     {
14168       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14169
14170       game.robot_wheel_x = x;
14171       game.robot_wheel_y = y;
14172       game.robot_wheel_active = TRUE;
14173
14174       TEST_DrawLevelField(x, y);
14175     }
14176     else if (element == EL_SP_TERMINAL)
14177     {
14178       int xx, yy;
14179
14180       SCAN_PLAYFIELD(xx, yy)
14181       {
14182         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14183         {
14184           Bang(xx, yy);
14185         }
14186         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14187         {
14188           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14189
14190           ResetGfxAnimation(xx, yy);
14191           TEST_DrawLevelField(xx, yy);
14192         }
14193       }
14194     }
14195     else if (IS_BELT_SWITCH(element))
14196     {
14197       ToggleBeltSwitch(x, y);
14198     }
14199     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14200              element == EL_SWITCHGATE_SWITCH_DOWN ||
14201              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14202              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14203     {
14204       ToggleSwitchgateSwitch(x, y);
14205     }
14206     else if (element == EL_LIGHT_SWITCH ||
14207              element == EL_LIGHT_SWITCH_ACTIVE)
14208     {
14209       ToggleLightSwitch(x, y);
14210     }
14211     else if (element == EL_TIMEGATE_SWITCH ||
14212              element == EL_DC_TIMEGATE_SWITCH)
14213     {
14214       ActivateTimegateSwitch(x, y);
14215     }
14216     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14217              element == EL_BALLOON_SWITCH_RIGHT ||
14218              element == EL_BALLOON_SWITCH_UP    ||
14219              element == EL_BALLOON_SWITCH_DOWN  ||
14220              element == EL_BALLOON_SWITCH_NONE  ||
14221              element == EL_BALLOON_SWITCH_ANY)
14222     {
14223       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14224                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14225                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14226                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14227                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14228                              move_direction);
14229     }
14230     else if (element == EL_LAMP)
14231     {
14232       Feld[x][y] = EL_LAMP_ACTIVE;
14233       game.lights_still_needed--;
14234
14235       ResetGfxAnimation(x, y);
14236       TEST_DrawLevelField(x, y);
14237     }
14238     else if (element == EL_TIME_ORB_FULL)
14239     {
14240       Feld[x][y] = EL_TIME_ORB_EMPTY;
14241
14242       if (level.time > 0 || level.use_time_orb_bug)
14243       {
14244         TimeLeft += level.time_orb_time;
14245         game.no_time_limit = FALSE;
14246
14247         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14248
14249         DisplayGameControlValues();
14250       }
14251
14252       ResetGfxAnimation(x, y);
14253       TEST_DrawLevelField(x, y);
14254     }
14255     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14256              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14257     {
14258       int xx, yy;
14259
14260       game.ball_active = !game.ball_active;
14261
14262       SCAN_PLAYFIELD(xx, yy)
14263       {
14264         int e = Feld[xx][yy];
14265
14266         if (game.ball_active)
14267         {
14268           if (e == EL_EMC_MAGIC_BALL)
14269             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14270           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14271             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14272         }
14273         else
14274         {
14275           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14276             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14277           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14278             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14279         }
14280       }
14281     }
14282
14283     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14284                                         player->index_bit, dig_side);
14285
14286     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14287                                         player->index_bit, dig_side);
14288
14289     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14290                                         player->index_bit, dig_side);
14291
14292     return MP_ACTION;
14293   }
14294   else
14295   {
14296     if (!PLAYER_SWITCHING(player, x, y))
14297     {
14298       player->is_switching = TRUE;
14299       player->switch_x = x;
14300       player->switch_y = y;
14301
14302       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14303                                  player->index_bit, dig_side);
14304       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14305                                           player->index_bit, dig_side);
14306
14307       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14308                                  player->index_bit, dig_side);
14309       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14310                                           player->index_bit, dig_side);
14311     }
14312
14313     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14314                                player->index_bit, dig_side);
14315     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14316                                         player->index_bit, dig_side);
14317
14318     return MP_NO_ACTION;
14319   }
14320
14321   player->push_delay = -1;
14322
14323   if (is_player)                // function can also be called by EL_PENGUIN
14324   {
14325     if (Feld[x][y] != element)          // really digged/collected something
14326     {
14327       player->is_collecting = !player->is_digging;
14328       player->is_active = TRUE;
14329     }
14330   }
14331
14332   return MP_MOVING;
14333 }
14334
14335 static boolean DigFieldByCE(int x, int y, int digging_element)
14336 {
14337   int element = Feld[x][y];
14338
14339   if (!IS_FREE(x, y))
14340   {
14341     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14342                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14343                   ACTION_BREAKING);
14344
14345     // no element can dig solid indestructible elements
14346     if (IS_INDESTRUCTIBLE(element) &&
14347         !IS_DIGGABLE(element) &&
14348         !IS_COLLECTIBLE(element))
14349       return FALSE;
14350
14351     if (AmoebaNr[x][y] &&
14352         (element == EL_AMOEBA_FULL ||
14353          element == EL_BD_AMOEBA ||
14354          element == EL_AMOEBA_GROWING))
14355     {
14356       AmoebaCnt[AmoebaNr[x][y]]--;
14357       AmoebaCnt2[AmoebaNr[x][y]]--;
14358     }
14359
14360     if (IS_MOVING(x, y))
14361       RemoveMovingField(x, y);
14362     else
14363     {
14364       RemoveField(x, y);
14365       TEST_DrawLevelField(x, y);
14366     }
14367
14368     // if digged element was about to explode, prevent the explosion
14369     ExplodeField[x][y] = EX_TYPE_NONE;
14370
14371     PlayLevelSoundAction(x, y, action);
14372   }
14373
14374   Store[x][y] = EL_EMPTY;
14375
14376   // this makes it possible to leave the removed element again
14377   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14378     Store[x][y] = element;
14379
14380   return TRUE;
14381 }
14382
14383 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14384 {
14385   int jx = player->jx, jy = player->jy;
14386   int x = jx + dx, y = jy + dy;
14387   int snap_direction = (dx == -1 ? MV_LEFT  :
14388                         dx == +1 ? MV_RIGHT :
14389                         dy == -1 ? MV_UP    :
14390                         dy == +1 ? MV_DOWN  : MV_NONE);
14391   boolean can_continue_snapping = (level.continuous_snapping &&
14392                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14393
14394   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14395     return FALSE;
14396
14397   if (!player->active || !IN_LEV_FIELD(x, y))
14398     return FALSE;
14399
14400   if (dx && dy)
14401     return FALSE;
14402
14403   if (!dx && !dy)
14404   {
14405     if (player->MovPos == 0)
14406       player->is_pushing = FALSE;
14407
14408     player->is_snapping = FALSE;
14409
14410     if (player->MovPos == 0)
14411     {
14412       player->is_moving = FALSE;
14413       player->is_digging = FALSE;
14414       player->is_collecting = FALSE;
14415     }
14416
14417     return FALSE;
14418   }
14419
14420   // prevent snapping with already pressed snap key when not allowed
14421   if (player->is_snapping && !can_continue_snapping)
14422     return FALSE;
14423
14424   player->MovDir = snap_direction;
14425
14426   if (player->MovPos == 0)
14427   {
14428     player->is_moving = FALSE;
14429     player->is_digging = FALSE;
14430     player->is_collecting = FALSE;
14431   }
14432
14433   player->is_dropping = FALSE;
14434   player->is_dropping_pressed = FALSE;
14435   player->drop_pressed_delay = 0;
14436
14437   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14438     return FALSE;
14439
14440   player->is_snapping = TRUE;
14441   player->is_active = TRUE;
14442
14443   if (player->MovPos == 0)
14444   {
14445     player->is_moving = FALSE;
14446     player->is_digging = FALSE;
14447     player->is_collecting = FALSE;
14448   }
14449
14450   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14451     TEST_DrawLevelField(player->last_jx, player->last_jy);
14452
14453   TEST_DrawLevelField(x, y);
14454
14455   return TRUE;
14456 }
14457
14458 static boolean DropElement(struct PlayerInfo *player)
14459 {
14460   int old_element, new_element;
14461   int dropx = player->jx, dropy = player->jy;
14462   int drop_direction = player->MovDir;
14463   int drop_side = drop_direction;
14464   int drop_element = get_next_dropped_element(player);
14465
14466   /* do not drop an element on top of another element; when holding drop key
14467      pressed without moving, dropped element must move away before the next
14468      element can be dropped (this is especially important if the next element
14469      is dynamite, which can be placed on background for historical reasons) */
14470   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14471     return MP_ACTION;
14472
14473   if (IS_THROWABLE(drop_element))
14474   {
14475     dropx += GET_DX_FROM_DIR(drop_direction);
14476     dropy += GET_DY_FROM_DIR(drop_direction);
14477
14478     if (!IN_LEV_FIELD(dropx, dropy))
14479       return FALSE;
14480   }
14481
14482   old_element = Feld[dropx][dropy];     // old element at dropping position
14483   new_element = drop_element;           // default: no change when dropping
14484
14485   // check if player is active, not moving and ready to drop
14486   if (!player->active || player->MovPos || player->drop_delay > 0)
14487     return FALSE;
14488
14489   // check if player has anything that can be dropped
14490   if (new_element == EL_UNDEFINED)
14491     return FALSE;
14492
14493   // only set if player has anything that can be dropped
14494   player->is_dropping_pressed = TRUE;
14495
14496   // check if drop key was pressed long enough for EM style dynamite
14497   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14498     return FALSE;
14499
14500   // check if anything can be dropped at the current position
14501   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14502     return FALSE;
14503
14504   // collected custom elements can only be dropped on empty fields
14505   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14506     return FALSE;
14507
14508   if (old_element != EL_EMPTY)
14509     Back[dropx][dropy] = old_element;   // store old element on this field
14510
14511   ResetGfxAnimation(dropx, dropy);
14512   ResetRandomAnimationValue(dropx, dropy);
14513
14514   if (player->inventory_size > 0 ||
14515       player->inventory_infinite_element != EL_UNDEFINED)
14516   {
14517     if (player->inventory_size > 0)
14518     {
14519       player->inventory_size--;
14520
14521       DrawGameDoorValues();
14522
14523       if (new_element == EL_DYNAMITE)
14524         new_element = EL_DYNAMITE_ACTIVE;
14525       else if (new_element == EL_EM_DYNAMITE)
14526         new_element = EL_EM_DYNAMITE_ACTIVE;
14527       else if (new_element == EL_SP_DISK_RED)
14528         new_element = EL_SP_DISK_RED_ACTIVE;
14529     }
14530
14531     Feld[dropx][dropy] = new_element;
14532
14533     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14534       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14535                           el2img(Feld[dropx][dropy]), 0);
14536
14537     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14538
14539     // needed if previous element just changed to "empty" in the last frame
14540     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14541
14542     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14543                                player->index_bit, drop_side);
14544     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14545                                         CE_PLAYER_DROPS_X,
14546                                         player->index_bit, drop_side);
14547
14548     TestIfElementTouchesCustomElement(dropx, dropy);
14549   }
14550   else          // player is dropping a dyna bomb
14551   {
14552     player->dynabombs_left--;
14553
14554     Feld[dropx][dropy] = new_element;
14555
14556     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14557       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14558                           el2img(Feld[dropx][dropy]), 0);
14559
14560     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14561   }
14562
14563   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14564     InitField_WithBug1(dropx, dropy, FALSE);
14565
14566   new_element = Feld[dropx][dropy];     // element might have changed
14567
14568   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14569       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14570   {
14571     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14572       MovDir[dropx][dropy] = drop_direction;
14573
14574     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14575
14576     // do not cause impact style collision by dropping elements that can fall
14577     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14578   }
14579
14580   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14581   player->is_dropping = TRUE;
14582
14583   player->drop_pressed_delay = 0;
14584   player->is_dropping_pressed = FALSE;
14585
14586   player->drop_x = dropx;
14587   player->drop_y = dropy;
14588
14589   return TRUE;
14590 }
14591
14592 // ----------------------------------------------------------------------------
14593 // game sound playing functions
14594 // ----------------------------------------------------------------------------
14595
14596 static int *loop_sound_frame = NULL;
14597 static int *loop_sound_volume = NULL;
14598
14599 void InitPlayLevelSound(void)
14600 {
14601   int num_sounds = getSoundListSize();
14602
14603   checked_free(loop_sound_frame);
14604   checked_free(loop_sound_volume);
14605
14606   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14607   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14608 }
14609
14610 static void PlayLevelSound(int x, int y, int nr)
14611 {
14612   int sx = SCREENX(x), sy = SCREENY(y);
14613   int volume, stereo_position;
14614   int max_distance = 8;
14615   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14616
14617   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14618       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14619     return;
14620
14621   if (!IN_LEV_FIELD(x, y) ||
14622       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14623       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14624     return;
14625
14626   volume = SOUND_MAX_VOLUME;
14627
14628   if (!IN_SCR_FIELD(sx, sy))
14629   {
14630     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14631     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14632
14633     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14634   }
14635
14636   stereo_position = (SOUND_MAX_LEFT +
14637                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14638                      (SCR_FIELDX + 2 * max_distance));
14639
14640   if (IS_LOOP_SOUND(nr))
14641   {
14642     /* This assures that quieter loop sounds do not overwrite louder ones,
14643        while restarting sound volume comparison with each new game frame. */
14644
14645     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14646       return;
14647
14648     loop_sound_volume[nr] = volume;
14649     loop_sound_frame[nr] = FrameCounter;
14650   }
14651
14652   PlaySoundExt(nr, volume, stereo_position, type);
14653 }
14654
14655 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14656 {
14657   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14658                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14659                  y < LEVELY(BY1) ? LEVELY(BY1) :
14660                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14661                  sound_action);
14662 }
14663
14664 static void PlayLevelSoundAction(int x, int y, int action)
14665 {
14666   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14667 }
14668
14669 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14670 {
14671   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14672
14673   if (sound_effect != SND_UNDEFINED)
14674     PlayLevelSound(x, y, sound_effect);
14675 }
14676
14677 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14678                                               int action)
14679 {
14680   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14681
14682   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14683     PlayLevelSound(x, y, sound_effect);
14684 }
14685
14686 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14687 {
14688   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14689
14690   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14691     PlayLevelSound(x, y, sound_effect);
14692 }
14693
14694 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14695 {
14696   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14697
14698   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14699     StopSound(sound_effect);
14700 }
14701
14702 static int getLevelMusicNr(void)
14703 {
14704   if (levelset.music[level_nr] != MUS_UNDEFINED)
14705     return levelset.music[level_nr];            // from config file
14706   else
14707     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14708 }
14709
14710 static void FadeLevelSounds(void)
14711 {
14712   FadeSounds();
14713 }
14714
14715 static void FadeLevelMusic(void)
14716 {
14717   int music_nr = getLevelMusicNr();
14718   char *curr_music = getCurrentlyPlayingMusicFilename();
14719   char *next_music = getMusicInfoEntryFilename(music_nr);
14720
14721   if (!strEqual(curr_music, next_music))
14722     FadeMusic();
14723 }
14724
14725 void FadeLevelSoundsAndMusic(void)
14726 {
14727   FadeLevelSounds();
14728   FadeLevelMusic();
14729 }
14730
14731 static void PlayLevelMusic(void)
14732 {
14733   int music_nr = getLevelMusicNr();
14734   char *curr_music = getCurrentlyPlayingMusicFilename();
14735   char *next_music = getMusicInfoEntryFilename(music_nr);
14736
14737   if (!strEqual(curr_music, next_music))
14738     PlayMusicLoop(music_nr);
14739 }
14740
14741 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14742 {
14743   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
14744   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14745   int x = xx - 1 - offset;
14746   int y = yy - 1 - offset;
14747
14748   switch (sample)
14749   {
14750     case SOUND_blank:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14752       break;
14753
14754     case SOUND_roll:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14756       break;
14757
14758     case SOUND_stone:
14759       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14760       break;
14761
14762     case SOUND_nut:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14764       break;
14765
14766     case SOUND_crack:
14767       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14768       break;
14769
14770     case SOUND_bug:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14772       break;
14773
14774     case SOUND_tank:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14776       break;
14777
14778     case SOUND_android_clone:
14779       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14780       break;
14781
14782     case SOUND_android_move:
14783       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14784       break;
14785
14786     case SOUND_spring:
14787       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14788       break;
14789
14790     case SOUND_slurp:
14791       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14792       break;
14793
14794     case SOUND_eater:
14795       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14796       break;
14797
14798     case SOUND_eater_eat:
14799       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14800       break;
14801
14802     case SOUND_alien:
14803       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14804       break;
14805
14806     case SOUND_collect:
14807       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14808       break;
14809
14810     case SOUND_diamond:
14811       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14812       break;
14813
14814     case SOUND_squash:
14815       // !!! CHECK THIS !!!
14816 #if 1
14817       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14818 #else
14819       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14820 #endif
14821       break;
14822
14823     case SOUND_wonderfall:
14824       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14825       break;
14826
14827     case SOUND_drip:
14828       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14829       break;
14830
14831     case SOUND_push:
14832       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14833       break;
14834
14835     case SOUND_dirt:
14836       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14837       break;
14838
14839     case SOUND_acid:
14840       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14841       break;
14842
14843     case SOUND_ball:
14844       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14845       break;
14846
14847     case SOUND_slide:
14848       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14849       break;
14850
14851     case SOUND_wonder:
14852       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14853       break;
14854
14855     case SOUND_door:
14856       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14857       break;
14858
14859     case SOUND_exit_open:
14860       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14861       break;
14862
14863     case SOUND_exit_leave:
14864       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14865       break;
14866
14867     case SOUND_dynamite:
14868       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14869       break;
14870
14871     case SOUND_tick:
14872       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14873       break;
14874
14875     case SOUND_press:
14876       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14877       break;
14878
14879     case SOUND_wheel:
14880       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14881       break;
14882
14883     case SOUND_boom:
14884       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14885       break;
14886
14887     case SOUND_die:
14888       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14889       break;
14890
14891     case SOUND_time:
14892       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14893       break;
14894
14895     default:
14896       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14897       break;
14898   }
14899 }
14900
14901 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14902 {
14903   int element = map_element_SP_to_RND(element_sp);
14904   int action = map_action_SP_to_RND(action_sp);
14905   int offset = (setup.sp_show_border_elements ? 0 : 1);
14906   int x = xx - offset;
14907   int y = yy - offset;
14908
14909   PlayLevelSoundElementAction(x, y, element, action);
14910 }
14911
14912 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14913 {
14914   int element = map_element_MM_to_RND(element_mm);
14915   int action = map_action_MM_to_RND(action_mm);
14916   int offset = 0;
14917   int x = xx - offset;
14918   int y = yy - offset;
14919
14920   if (!IS_MM_ELEMENT(element))
14921     element = EL_MM_DEFAULT;
14922
14923   PlayLevelSoundElementAction(x, y, element, action);
14924 }
14925
14926 void PlaySound_MM(int sound_mm)
14927 {
14928   int sound = map_sound_MM_to_RND(sound_mm);
14929
14930   if (sound == SND_UNDEFINED)
14931     return;
14932
14933   PlaySound(sound);
14934 }
14935
14936 void PlaySoundLoop_MM(int sound_mm)
14937 {
14938   int sound = map_sound_MM_to_RND(sound_mm);
14939
14940   if (sound == SND_UNDEFINED)
14941     return;
14942
14943   PlaySoundLoop(sound);
14944 }
14945
14946 void StopSound_MM(int sound_mm)
14947 {
14948   int sound = map_sound_MM_to_RND(sound_mm);
14949
14950   if (sound == SND_UNDEFINED)
14951     return;
14952
14953   StopSound(sound);
14954 }
14955
14956 void RaiseScore(int value)
14957 {
14958   game.score += value;
14959
14960   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14961
14962   DisplayGameControlValues();
14963 }
14964
14965 void RaiseScoreElement(int element)
14966 {
14967   switch (element)
14968   {
14969     case EL_EMERALD:
14970     case EL_BD_DIAMOND:
14971     case EL_EMERALD_YELLOW:
14972     case EL_EMERALD_RED:
14973     case EL_EMERALD_PURPLE:
14974     case EL_SP_INFOTRON:
14975       RaiseScore(level.score[SC_EMERALD]);
14976       break;
14977     case EL_DIAMOND:
14978       RaiseScore(level.score[SC_DIAMOND]);
14979       break;
14980     case EL_CRYSTAL:
14981       RaiseScore(level.score[SC_CRYSTAL]);
14982       break;
14983     case EL_PEARL:
14984       RaiseScore(level.score[SC_PEARL]);
14985       break;
14986     case EL_BUG:
14987     case EL_BD_BUTTERFLY:
14988     case EL_SP_ELECTRON:
14989       RaiseScore(level.score[SC_BUG]);
14990       break;
14991     case EL_SPACESHIP:
14992     case EL_BD_FIREFLY:
14993     case EL_SP_SNIKSNAK:
14994       RaiseScore(level.score[SC_SPACESHIP]);
14995       break;
14996     case EL_YAMYAM:
14997     case EL_DARK_YAMYAM:
14998       RaiseScore(level.score[SC_YAMYAM]);
14999       break;
15000     case EL_ROBOT:
15001       RaiseScore(level.score[SC_ROBOT]);
15002       break;
15003     case EL_PACMAN:
15004       RaiseScore(level.score[SC_PACMAN]);
15005       break;
15006     case EL_NUT:
15007       RaiseScore(level.score[SC_NUT]);
15008       break;
15009     case EL_DYNAMITE:
15010     case EL_EM_DYNAMITE:
15011     case EL_SP_DISK_RED:
15012     case EL_DYNABOMB_INCREASE_NUMBER:
15013     case EL_DYNABOMB_INCREASE_SIZE:
15014     case EL_DYNABOMB_INCREASE_POWER:
15015       RaiseScore(level.score[SC_DYNAMITE]);
15016       break;
15017     case EL_SHIELD_NORMAL:
15018     case EL_SHIELD_DEADLY:
15019       RaiseScore(level.score[SC_SHIELD]);
15020       break;
15021     case EL_EXTRA_TIME:
15022       RaiseScore(level.extra_time_score);
15023       break;
15024     case EL_KEY_1:
15025     case EL_KEY_2:
15026     case EL_KEY_3:
15027     case EL_KEY_4:
15028     case EL_EM_KEY_1:
15029     case EL_EM_KEY_2:
15030     case EL_EM_KEY_3:
15031     case EL_EM_KEY_4:
15032     case EL_EMC_KEY_5:
15033     case EL_EMC_KEY_6:
15034     case EL_EMC_KEY_7:
15035     case EL_EMC_KEY_8:
15036     case EL_DC_KEY_WHITE:
15037       RaiseScore(level.score[SC_KEY]);
15038       break;
15039     default:
15040       RaiseScore(element_info[element].collect_score);
15041       break;
15042   }
15043 }
15044
15045 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15046 {
15047   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15048   {
15049     // closing door required in case of envelope style request dialogs
15050     if (!skip_request)
15051     {
15052       // prevent short reactivation of overlay buttons while closing door
15053       SetOverlayActive(FALSE);
15054
15055       CloseDoor(DOOR_CLOSE_1);
15056     }
15057
15058     if (network.enabled)
15059       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15060     else
15061     {
15062       if (quick_quit)
15063         FadeSkipNextFadeIn();
15064
15065       SetGameStatus(GAME_MODE_MAIN);
15066
15067       DrawMainMenu();
15068     }
15069   }
15070   else          // continue playing the game
15071   {
15072     if (tape.playing && tape.deactivate_display)
15073       TapeDeactivateDisplayOff(TRUE);
15074
15075     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15076
15077     if (tape.playing && tape.deactivate_display)
15078       TapeDeactivateDisplayOn();
15079   }
15080 }
15081
15082 void RequestQuitGame(boolean ask_if_really_quit)
15083 {
15084   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15085   boolean skip_request = game.all_players_gone || quick_quit;
15086
15087   RequestQuitGameExt(skip_request, quick_quit,
15088                      "Do you really want to quit the game?");
15089 }
15090
15091 void RequestRestartGame(char *message)
15092 {
15093   game.restart_game_message = NULL;
15094
15095   boolean has_started_game = hasStartedNetworkGame();
15096   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15097
15098   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15099   {
15100     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15101   }
15102   else
15103   {
15104     SetGameStatus(GAME_MODE_MAIN);
15105
15106     DrawMainMenu();
15107   }
15108 }
15109
15110 void CheckGameOver(void)
15111 {
15112   static boolean last_game_over = FALSE;
15113   static int game_over_delay = 0;
15114   int game_over_delay_value = 50;
15115   boolean game_over = checkGameFailed();
15116
15117   // do not handle game over if request dialog is already active
15118   if (game.request_active)
15119     return;
15120
15121   // do not ask to play again if game was never actually played
15122   if (!game.GamePlayed)
15123     return;
15124
15125   if (!game_over)
15126   {
15127     last_game_over = FALSE;
15128     game_over_delay = game_over_delay_value;
15129
15130     return;
15131   }
15132
15133   if (game_over_delay > 0)
15134   {
15135     game_over_delay--;
15136
15137     return;
15138   }
15139
15140   if (last_game_over != game_over)
15141     game.restart_game_message = (hasStartedNetworkGame() ?
15142                                  "Game over! Play it again?" :
15143                                  "Game over!");
15144
15145   last_game_over = game_over;
15146 }
15147
15148 boolean checkGameSolved(void)
15149 {
15150   // set for all game engines if level was solved
15151   return game.LevelSolved_GameEnd;
15152 }
15153
15154 boolean checkGameFailed(void)
15155 {
15156   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15157     return (game_em.game_over && !game_em.level_solved);
15158   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15159     return (game_sp.game_over && !game_sp.level_solved);
15160   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15161     return (game_mm.game_over && !game_mm.level_solved);
15162   else                          // GAME_ENGINE_TYPE_RND
15163     return (game.GameOver && !game.LevelSolved);
15164 }
15165
15166 boolean checkGameEnded(void)
15167 {
15168   return (checkGameSolved() || checkGameFailed());
15169 }
15170
15171
15172 // ----------------------------------------------------------------------------
15173 // random generator functions
15174 // ----------------------------------------------------------------------------
15175
15176 unsigned int InitEngineRandom_RND(int seed)
15177 {
15178   game.num_random_calls = 0;
15179
15180   return InitEngineRandom(seed);
15181 }
15182
15183 unsigned int RND(int max)
15184 {
15185   if (max > 0)
15186   {
15187     game.num_random_calls++;
15188
15189     return GetEngineRandom(max);
15190   }
15191
15192   return 0;
15193 }
15194
15195
15196 // ----------------------------------------------------------------------------
15197 // game engine snapshot handling functions
15198 // ----------------------------------------------------------------------------
15199
15200 struct EngineSnapshotInfo
15201 {
15202   // runtime values for custom element collect score
15203   int collect_score[NUM_CUSTOM_ELEMENTS];
15204
15205   // runtime values for group element choice position
15206   int choice_pos[NUM_GROUP_ELEMENTS];
15207
15208   // runtime values for belt position animations
15209   int belt_graphic[4][NUM_BELT_PARTS];
15210   int belt_anim_mode[4][NUM_BELT_PARTS];
15211 };
15212
15213 static struct EngineSnapshotInfo engine_snapshot_rnd;
15214 static char *snapshot_level_identifier = NULL;
15215 static int snapshot_level_nr = -1;
15216
15217 static void SaveEngineSnapshotValues_RND(void)
15218 {
15219   static int belt_base_active_element[4] =
15220   {
15221     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15222     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15223     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15224     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15225   };
15226   int i, j;
15227
15228   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15229   {
15230     int element = EL_CUSTOM_START + i;
15231
15232     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15233   }
15234
15235   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15236   {
15237     int element = EL_GROUP_START + i;
15238
15239     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15240   }
15241
15242   for (i = 0; i < 4; i++)
15243   {
15244     for (j = 0; j < NUM_BELT_PARTS; j++)
15245     {
15246       int element = belt_base_active_element[i] + j;
15247       int graphic = el2img(element);
15248       int anim_mode = graphic_info[graphic].anim_mode;
15249
15250       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15251       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15252     }
15253   }
15254 }
15255
15256 static void LoadEngineSnapshotValues_RND(void)
15257 {
15258   unsigned int num_random_calls = game.num_random_calls;
15259   int i, j;
15260
15261   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15262   {
15263     int element = EL_CUSTOM_START + i;
15264
15265     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15266   }
15267
15268   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15269   {
15270     int element = EL_GROUP_START + i;
15271
15272     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15273   }
15274
15275   for (i = 0; i < 4; i++)
15276   {
15277     for (j = 0; j < NUM_BELT_PARTS; j++)
15278     {
15279       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15280       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15281
15282       graphic_info[graphic].anim_mode = anim_mode;
15283     }
15284   }
15285
15286   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15287   {
15288     InitRND(tape.random_seed);
15289     for (i = 0; i < num_random_calls; i++)
15290       RND(1);
15291   }
15292
15293   if (game.num_random_calls != num_random_calls)
15294   {
15295     Error(ERR_INFO, "number of random calls out of sync");
15296     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15297     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15298     Error(ERR_EXIT, "this should not happen -- please debug");
15299   }
15300 }
15301
15302 void FreeEngineSnapshotSingle(void)
15303 {
15304   FreeSnapshotSingle();
15305
15306   setString(&snapshot_level_identifier, NULL);
15307   snapshot_level_nr = -1;
15308 }
15309
15310 void FreeEngineSnapshotList(void)
15311 {
15312   FreeSnapshotList();
15313 }
15314
15315 static ListNode *SaveEngineSnapshotBuffers(void)
15316 {
15317   ListNode *buffers = NULL;
15318
15319   // copy some special values to a structure better suited for the snapshot
15320
15321   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15322     SaveEngineSnapshotValues_RND();
15323   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15324     SaveEngineSnapshotValues_EM();
15325   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15326     SaveEngineSnapshotValues_SP(&buffers);
15327   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15328     SaveEngineSnapshotValues_MM(&buffers);
15329
15330   // save values stored in special snapshot structure
15331
15332   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15333     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15334   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15335     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15336   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15337     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15338   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15339     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15340
15341   // save further RND engine values
15342
15343   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15344   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15345   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15346
15347   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15348   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15349   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15350   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15351   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15352
15353   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15354   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15355   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15356
15357   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15358
15359   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15360   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15361
15362   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15363   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15364   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15365   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15366   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15367   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15368   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15369   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15370   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15371   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15372   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15373   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15374   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15375   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15376   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15377   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15378   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15379   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15380
15381   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15382   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15383
15384   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15385   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15386   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15387
15388   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15389   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15390
15391   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15392   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15393   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15394   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15395   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15396
15397   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15398   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15399
15400 #if 0
15401   ListNode *node = engine_snapshot_list_rnd;
15402   int num_bytes = 0;
15403
15404   while (node != NULL)
15405   {
15406     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15407
15408     node = node->next;
15409   }
15410
15411   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15412 #endif
15413
15414   return buffers;
15415 }
15416
15417 void SaveEngineSnapshotSingle(void)
15418 {
15419   ListNode *buffers = SaveEngineSnapshotBuffers();
15420
15421   // finally save all snapshot buffers to single snapshot
15422   SaveSnapshotSingle(buffers);
15423
15424   // save level identification information
15425   setString(&snapshot_level_identifier, leveldir_current->identifier);
15426   snapshot_level_nr = level_nr;
15427 }
15428
15429 boolean CheckSaveEngineSnapshotToList(void)
15430 {
15431   boolean save_snapshot =
15432     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15433      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15434       game.snapshot.changed_action) ||
15435      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15436       game.snapshot.collected_item));
15437
15438   game.snapshot.changed_action = FALSE;
15439   game.snapshot.collected_item = FALSE;
15440   game.snapshot.save_snapshot = save_snapshot;
15441
15442   return save_snapshot;
15443 }
15444
15445 void SaveEngineSnapshotToList(void)
15446 {
15447   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15448       tape.quick_resume)
15449     return;
15450
15451   ListNode *buffers = SaveEngineSnapshotBuffers();
15452
15453   // finally save all snapshot buffers to snapshot list
15454   SaveSnapshotToList(buffers);
15455 }
15456
15457 void SaveEngineSnapshotToListInitial(void)
15458 {
15459   FreeEngineSnapshotList();
15460
15461   SaveEngineSnapshotToList();
15462 }
15463
15464 static void LoadEngineSnapshotValues(void)
15465 {
15466   // restore special values from snapshot structure
15467
15468   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15469     LoadEngineSnapshotValues_RND();
15470   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15471     LoadEngineSnapshotValues_EM();
15472   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15473     LoadEngineSnapshotValues_SP();
15474   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15475     LoadEngineSnapshotValues_MM();
15476 }
15477
15478 void LoadEngineSnapshotSingle(void)
15479 {
15480   LoadSnapshotSingle();
15481
15482   LoadEngineSnapshotValues();
15483 }
15484
15485 static void LoadEngineSnapshot_Undo(int steps)
15486 {
15487   LoadSnapshotFromList_Older(steps);
15488
15489   LoadEngineSnapshotValues();
15490 }
15491
15492 static void LoadEngineSnapshot_Redo(int steps)
15493 {
15494   LoadSnapshotFromList_Newer(steps);
15495
15496   LoadEngineSnapshotValues();
15497 }
15498
15499 boolean CheckEngineSnapshotSingle(void)
15500 {
15501   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15502           snapshot_level_nr == level_nr);
15503 }
15504
15505 boolean CheckEngineSnapshotList(void)
15506 {
15507   return CheckSnapshotList();
15508 }
15509
15510
15511 // ---------- new game button stuff -------------------------------------------
15512
15513 static struct
15514 {
15515   int graphic;
15516   struct XY *pos;
15517   int gadget_id;
15518   boolean *setup_value;
15519   boolean allowed_on_tape;
15520   boolean is_touch_button;
15521   char *infotext;
15522 } gamebutton_info[NUM_GAME_BUTTONS] =
15523 {
15524   {
15525     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15526     GAME_CTRL_ID_STOP,                          NULL,
15527     TRUE, FALSE,                                "stop game"
15528   },
15529   {
15530     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15531     GAME_CTRL_ID_PAUSE,                         NULL,
15532     TRUE, FALSE,                                "pause game"
15533   },
15534   {
15535     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15536     GAME_CTRL_ID_PLAY,                          NULL,
15537     TRUE, FALSE,                                "play game"
15538   },
15539   {
15540     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15541     GAME_CTRL_ID_UNDO,                          NULL,
15542     TRUE, FALSE,                                "undo step"
15543   },
15544   {
15545     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15546     GAME_CTRL_ID_REDO,                          NULL,
15547     TRUE, FALSE,                                "redo step"
15548   },
15549   {
15550     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15551     GAME_CTRL_ID_SAVE,                          NULL,
15552     TRUE, FALSE,                                "save game"
15553   },
15554   {
15555     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15556     GAME_CTRL_ID_PAUSE2,                        NULL,
15557     TRUE, FALSE,                                "pause game"
15558   },
15559   {
15560     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15561     GAME_CTRL_ID_LOAD,                          NULL,
15562     TRUE, FALSE,                                "load game"
15563   },
15564   {
15565     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15566     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15567     FALSE, FALSE,                               "stop game"
15568   },
15569   {
15570     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15571     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15572     FALSE, FALSE,                               "pause game"
15573   },
15574   {
15575     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15576     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15577     FALSE, FALSE,                               "play game"
15578   },
15579   {
15580     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15581     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15582     FALSE, TRUE,                                "stop game"
15583   },
15584   {
15585     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15586     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15587     FALSE, TRUE,                                "pause game"
15588   },
15589   {
15590     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15591     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15592     TRUE, FALSE,                                "background music on/off"
15593   },
15594   {
15595     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15596     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15597     TRUE, FALSE,                                "sound loops on/off"
15598   },
15599   {
15600     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15601     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15602     TRUE, FALSE,                                "normal sounds on/off"
15603   },
15604   {
15605     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15606     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15607     FALSE, FALSE,                               "background music on/off"
15608   },
15609   {
15610     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15611     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15612     FALSE, FALSE,                               "sound loops on/off"
15613   },
15614   {
15615     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15616     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15617     FALSE, FALSE,                               "normal sounds on/off"
15618   }
15619 };
15620
15621 void CreateGameButtons(void)
15622 {
15623   int i;
15624
15625   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15626   {
15627     int graphic = gamebutton_info[i].graphic;
15628     struct GraphicInfo *gfx = &graphic_info[graphic];
15629     struct XY *pos = gamebutton_info[i].pos;
15630     struct GadgetInfo *gi;
15631     int button_type;
15632     boolean checked;
15633     unsigned int event_mask;
15634     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15635     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15636     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15637     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15638     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15639     int gd_x   = gfx->src_x;
15640     int gd_y   = gfx->src_y;
15641     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15642     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15643     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15644     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15645     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15646     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15647     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15648     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15649     int id = i;
15650
15651     if (gfx->bitmap == NULL)
15652     {
15653       game_gadget[id] = NULL;
15654
15655       continue;
15656     }
15657
15658     if (id == GAME_CTRL_ID_STOP ||
15659         id == GAME_CTRL_ID_PANEL_STOP ||
15660         id == GAME_CTRL_ID_TOUCH_STOP ||
15661         id == GAME_CTRL_ID_PLAY ||
15662         id == GAME_CTRL_ID_PANEL_PLAY ||
15663         id == GAME_CTRL_ID_SAVE ||
15664         id == GAME_CTRL_ID_LOAD)
15665     {
15666       button_type = GD_TYPE_NORMAL_BUTTON;
15667       checked = FALSE;
15668       event_mask = GD_EVENT_RELEASED;
15669     }
15670     else if (id == GAME_CTRL_ID_UNDO ||
15671              id == GAME_CTRL_ID_REDO)
15672     {
15673       button_type = GD_TYPE_NORMAL_BUTTON;
15674       checked = FALSE;
15675       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15676     }
15677     else
15678     {
15679       button_type = GD_TYPE_CHECK_BUTTON;
15680       checked = (gamebutton_info[i].setup_value != NULL ?
15681                  *gamebutton_info[i].setup_value : FALSE);
15682       event_mask = GD_EVENT_PRESSED;
15683     }
15684
15685     gi = CreateGadget(GDI_CUSTOM_ID, id,
15686                       GDI_IMAGE_ID, graphic,
15687                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15688                       GDI_X, base_x + x,
15689                       GDI_Y, base_y + y,
15690                       GDI_WIDTH, gfx->width,
15691                       GDI_HEIGHT, gfx->height,
15692                       GDI_TYPE, button_type,
15693                       GDI_STATE, GD_BUTTON_UNPRESSED,
15694                       GDI_CHECKED, checked,
15695                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15696                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15697                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15698                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15699                       GDI_DIRECT_DRAW, FALSE,
15700                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15701                       GDI_EVENT_MASK, event_mask,
15702                       GDI_CALLBACK_ACTION, HandleGameButtons,
15703                       GDI_END);
15704
15705     if (gi == NULL)
15706       Error(ERR_EXIT, "cannot create gadget");
15707
15708     game_gadget[id] = gi;
15709   }
15710 }
15711
15712 void FreeGameButtons(void)
15713 {
15714   int i;
15715
15716   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15717     FreeGadget(game_gadget[i]);
15718 }
15719
15720 static void UnmapGameButtonsAtSamePosition(int id)
15721 {
15722   int i;
15723
15724   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15725     if (i != id &&
15726         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15727         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15728       UnmapGadget(game_gadget[i]);
15729 }
15730
15731 static void UnmapGameButtonsAtSamePosition_All(void)
15732 {
15733   if (setup.show_snapshot_buttons)
15734   {
15735     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15736     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15737     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15738   }
15739   else
15740   {
15741     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15742     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15743     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15744
15745     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15746     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15747     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15748   }
15749 }
15750
15751 static void MapGameButtonsAtSamePosition(int id)
15752 {
15753   int i;
15754
15755   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15756     if (i != id &&
15757         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15758         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15759       MapGadget(game_gadget[i]);
15760
15761   UnmapGameButtonsAtSamePosition_All();
15762 }
15763
15764 void MapUndoRedoButtons(void)
15765 {
15766   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15767   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15768
15769   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15770   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15771 }
15772
15773 void UnmapUndoRedoButtons(void)
15774 {
15775   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15776   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15777
15778   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15779   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15780 }
15781
15782 void ModifyPauseButtons(void)
15783 {
15784   static int ids[] =
15785   {
15786     GAME_CTRL_ID_PAUSE,
15787     GAME_CTRL_ID_PAUSE2,
15788     GAME_CTRL_ID_PANEL_PAUSE,
15789     GAME_CTRL_ID_TOUCH_PAUSE,
15790     -1
15791   };
15792   int i;
15793
15794   for (i = 0; ids[i] > -1; i++)
15795     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15796 }
15797
15798 static void MapGameButtonsExt(boolean on_tape)
15799 {
15800   int i;
15801
15802   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15803     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15804         i != GAME_CTRL_ID_UNDO &&
15805         i != GAME_CTRL_ID_REDO)
15806       MapGadget(game_gadget[i]);
15807
15808   UnmapGameButtonsAtSamePosition_All();
15809
15810   RedrawGameButtons();
15811 }
15812
15813 static void UnmapGameButtonsExt(boolean on_tape)
15814 {
15815   int i;
15816
15817   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15818     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15819       UnmapGadget(game_gadget[i]);
15820 }
15821
15822 static void RedrawGameButtonsExt(boolean on_tape)
15823 {
15824   int i;
15825
15826   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15827     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15828       RedrawGadget(game_gadget[i]);
15829 }
15830
15831 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15832 {
15833   if (gi == NULL)
15834     return;
15835
15836   gi->checked = state;
15837 }
15838
15839 static void RedrawSoundButtonGadget(int id)
15840 {
15841   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15842              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15843              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15844              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15845              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15846              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15847              id);
15848
15849   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15850   RedrawGadget(game_gadget[id2]);
15851 }
15852
15853 void MapGameButtons(void)
15854 {
15855   MapGameButtonsExt(FALSE);
15856 }
15857
15858 void UnmapGameButtons(void)
15859 {
15860   UnmapGameButtonsExt(FALSE);
15861 }
15862
15863 void RedrawGameButtons(void)
15864 {
15865   RedrawGameButtonsExt(FALSE);
15866 }
15867
15868 void MapGameButtonsOnTape(void)
15869 {
15870   MapGameButtonsExt(TRUE);
15871 }
15872
15873 void UnmapGameButtonsOnTape(void)
15874 {
15875   UnmapGameButtonsExt(TRUE);
15876 }
15877
15878 void RedrawGameButtonsOnTape(void)
15879 {
15880   RedrawGameButtonsExt(TRUE);
15881 }
15882
15883 static void GameUndoRedoExt(void)
15884 {
15885   ClearPlayerAction();
15886
15887   tape.pausing = TRUE;
15888
15889   RedrawPlayfield();
15890   UpdateAndDisplayGameControlValues();
15891
15892   DrawCompleteVideoDisplay();
15893   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15894   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15895   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15896
15897   BackToFront();
15898 }
15899
15900 static void GameUndo(int steps)
15901 {
15902   if (!CheckEngineSnapshotList())
15903     return;
15904
15905   LoadEngineSnapshot_Undo(steps);
15906
15907   GameUndoRedoExt();
15908 }
15909
15910 static void GameRedo(int steps)
15911 {
15912   if (!CheckEngineSnapshotList())
15913     return;
15914
15915   LoadEngineSnapshot_Redo(steps);
15916
15917   GameUndoRedoExt();
15918 }
15919
15920 static void HandleGameButtonsExt(int id, int button)
15921 {
15922   static boolean game_undo_executed = FALSE;
15923   int steps = BUTTON_STEPSIZE(button);
15924   boolean handle_game_buttons =
15925     (game_status == GAME_MODE_PLAYING ||
15926      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15927
15928   if (!handle_game_buttons)
15929     return;
15930
15931   switch (id)
15932   {
15933     case GAME_CTRL_ID_STOP:
15934     case GAME_CTRL_ID_PANEL_STOP:
15935     case GAME_CTRL_ID_TOUCH_STOP:
15936       if (game_status == GAME_MODE_MAIN)
15937         break;
15938
15939       if (tape.playing)
15940         TapeStop();
15941       else
15942         RequestQuitGame(TRUE);
15943
15944       break;
15945
15946     case GAME_CTRL_ID_PAUSE:
15947     case GAME_CTRL_ID_PAUSE2:
15948     case GAME_CTRL_ID_PANEL_PAUSE:
15949     case GAME_CTRL_ID_TOUCH_PAUSE:
15950       if (network.enabled && game_status == GAME_MODE_PLAYING)
15951       {
15952         if (tape.pausing)
15953           SendToServer_ContinuePlaying();
15954         else
15955           SendToServer_PausePlaying();
15956       }
15957       else
15958         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15959
15960       game_undo_executed = FALSE;
15961
15962       break;
15963
15964     case GAME_CTRL_ID_PLAY:
15965     case GAME_CTRL_ID_PANEL_PLAY:
15966       if (game_status == GAME_MODE_MAIN)
15967       {
15968         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15969       }
15970       else if (tape.pausing)
15971       {
15972         if (network.enabled)
15973           SendToServer_ContinuePlaying();
15974         else
15975           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15976       }
15977       break;
15978
15979     case GAME_CTRL_ID_UNDO:
15980       // Important: When using "save snapshot when collecting an item" mode,
15981       // load last (current) snapshot for first "undo" after pressing "pause"
15982       // (else the last-but-one snapshot would be loaded, because the snapshot
15983       // pointer already points to the last snapshot when pressing "pause",
15984       // which is fine for "every step/move" mode, but not for "every collect")
15985       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15986           !game_undo_executed)
15987         steps--;
15988
15989       game_undo_executed = TRUE;
15990
15991       GameUndo(steps);
15992       break;
15993
15994     case GAME_CTRL_ID_REDO:
15995       GameRedo(steps);
15996       break;
15997
15998     case GAME_CTRL_ID_SAVE:
15999       TapeQuickSave();
16000       break;
16001
16002     case GAME_CTRL_ID_LOAD:
16003       TapeQuickLoad();
16004       break;
16005
16006     case SOUND_CTRL_ID_MUSIC:
16007     case SOUND_CTRL_ID_PANEL_MUSIC:
16008       if (setup.sound_music)
16009       { 
16010         setup.sound_music = FALSE;
16011
16012         FadeMusic();
16013       }
16014       else if (audio.music_available)
16015       { 
16016         setup.sound = setup.sound_music = TRUE;
16017
16018         SetAudioMode(setup.sound);
16019
16020         if (game_status == GAME_MODE_PLAYING)
16021           PlayLevelMusic();
16022       }
16023
16024       RedrawSoundButtonGadget(id);
16025
16026       break;
16027
16028     case SOUND_CTRL_ID_LOOPS:
16029     case SOUND_CTRL_ID_PANEL_LOOPS:
16030       if (setup.sound_loops)
16031         setup.sound_loops = FALSE;
16032       else if (audio.loops_available)
16033       {
16034         setup.sound = setup.sound_loops = TRUE;
16035
16036         SetAudioMode(setup.sound);
16037       }
16038
16039       RedrawSoundButtonGadget(id);
16040
16041       break;
16042
16043     case SOUND_CTRL_ID_SIMPLE:
16044     case SOUND_CTRL_ID_PANEL_SIMPLE:
16045       if (setup.sound_simple)
16046         setup.sound_simple = FALSE;
16047       else if (audio.sound_available)
16048       {
16049         setup.sound = setup.sound_simple = TRUE;
16050
16051         SetAudioMode(setup.sound);
16052       }
16053
16054       RedrawSoundButtonGadget(id);
16055
16056       break;
16057
16058     default:
16059       break;
16060   }
16061 }
16062
16063 static void HandleGameButtons(struct GadgetInfo *gi)
16064 {
16065   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16066 }
16067
16068 void HandleSoundButtonKeys(Key key)
16069 {
16070   if (key == setup.shortcut.sound_simple)
16071     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16072   else if (key == setup.shortcut.sound_loops)
16073     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16074   else if (key == setup.shortcut.sound_music)
16075     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16076 }