added fast player relocation for level wrap-around in EM engine
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 // game button identifiers
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define GAME_CTRL_ID_TOUCH_STOP         11
1020 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1021 #define SOUND_CTRL_ID_MUSIC             13
1022 #define SOUND_CTRL_ID_LOOPS             14
1023 #define SOUND_CTRL_ID_SIMPLE            15
1024 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1025 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1026 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1027
1028 #define NUM_GAME_BUTTONS                19
1029
1030
1031 // forward declaration for internal use
1032
1033 static void CreateField(int, int, int);
1034
1035 static void ResetGfxAnimation(int, int);
1036
1037 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1038 static void AdvanceFrameAndPlayerCounters(int);
1039
1040 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1041 static boolean MovePlayer(struct PlayerInfo *, int, int);
1042 static void ScrollPlayer(struct PlayerInfo *, int);
1043 static void ScrollScreen(struct PlayerInfo *, int);
1044
1045 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1046 static boolean DigFieldByCE(int, int, int);
1047 static boolean SnapField(struct PlayerInfo *, int, int);
1048 static boolean DropElement(struct PlayerInfo *);
1049
1050 static void InitBeltMovement(void);
1051 static void CloseAllOpenTimegates(void);
1052 static void CheckGravityMovement(struct PlayerInfo *);
1053 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1054 static void KillPlayerUnlessEnemyProtected(int, int);
1055 static void KillPlayerUnlessExplosionProtected(int, int);
1056
1057 static void TestIfPlayerTouchesCustomElement(int, int);
1058 static void TestIfElementTouchesCustomElement(int, int);
1059 static void TestIfElementHitsCustomElement(int, int, int);
1060
1061 static void HandleElementChange(int, int, int);
1062 static void ExecuteCustomElementAction(int, int, int, int);
1063 static boolean ChangeElement(int, int, int, int);
1064
1065 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1066 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1067         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1068 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1070 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1071         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1072 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1073         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1074
1075 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1076 #define CheckElementChange(x, y, e, te, ev)                             \
1077         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1078 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1079         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1080 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1082
1083 static void PlayLevelSound(int, int, int);
1084 static void PlayLevelSoundNearest(int, int, int);
1085 static void PlayLevelSoundAction(int, int, int);
1086 static void PlayLevelSoundElementAction(int, int, int, int);
1087 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1088 static void PlayLevelSoundActionIfLoop(int, int, int);
1089 static void StopLevelSoundActionIfLoop(int, int, int);
1090 static void PlayLevelMusic(void);
1091 static void FadeLevelSoundsAndMusic(void);
1092
1093 static void HandleGameButtons(struct GadgetInfo *);
1094
1095 int AmoebeNachbarNr(int, int);
1096 void AmoebeUmwandeln(int, int);
1097 void ContinueMoving(int, int);
1098 void Bang(int, int);
1099 void InitMovDir(int, int);
1100 void InitAmoebaNr(int, int);
1101 int NewHiScore(int);
1102
1103 void TestIfGoodThingHitsBadThing(int, int, int);
1104 void TestIfBadThingHitsGoodThing(int, int, int);
1105 void TestIfPlayerTouchesBadThing(int, int);
1106 void TestIfPlayerRunsIntoBadThing(int, int, int);
1107 void TestIfBadThingTouchesPlayer(int, int);
1108 void TestIfBadThingRunsIntoPlayer(int, int, int);
1109 void TestIfFriendTouchesBadThing(int, int);
1110 void TestIfBadThingTouchesFriend(int, int);
1111 void TestIfBadThingTouchesOtherBadThing(int, int);
1112 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1113
1114 void KillPlayer(struct PlayerInfo *);
1115 void BuryPlayer(struct PlayerInfo *);
1116 void RemovePlayer(struct PlayerInfo *);
1117 void ExitPlayer(struct PlayerInfo *);
1118
1119 static int getInvisibleActiveFromInvisibleElement(int);
1120 static int getInvisibleFromInvisibleActiveElement(int);
1121
1122 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1123
1124 // for detection of endless loops, caused by custom element programming
1125 // (using maximal playfield width x 10 is just a rough approximation)
1126 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1127
1128 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1129 {                                                                       \
1130   if (recursion_loop_detected)                                          \
1131     return (rc);                                                        \
1132                                                                         \
1133   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1134   {                                                                     \
1135     recursion_loop_detected = TRUE;                                     \
1136     recursion_loop_element = (e);                                       \
1137   }                                                                     \
1138                                                                         \
1139   recursion_loop_depth++;                                               \
1140 }
1141
1142 #define RECURSION_LOOP_DETECTION_END()                                  \
1143 {                                                                       \
1144   recursion_loop_depth--;                                               \
1145 }
1146
1147 static int recursion_loop_depth;
1148 static boolean recursion_loop_detected;
1149 static boolean recursion_loop_element;
1150
1151 static int map_player_action[MAX_PLAYERS];
1152
1153
1154 // ----------------------------------------------------------------------------
1155 // definition of elements that automatically change to other elements after
1156 // a specified time, eventually calling a function when changing
1157 // ----------------------------------------------------------------------------
1158
1159 // forward declaration for changer functions
1160 static void InitBuggyBase(int, int);
1161 static void WarnBuggyBase(int, int);
1162
1163 static void InitTrap(int, int);
1164 static void ActivateTrap(int, int);
1165 static void ChangeActiveTrap(int, int);
1166
1167 static void InitRobotWheel(int, int);
1168 static void RunRobotWheel(int, int);
1169 static void StopRobotWheel(int, int);
1170
1171 static void InitTimegateWheel(int, int);
1172 static void RunTimegateWheel(int, int);
1173
1174 static void InitMagicBallDelay(int, int);
1175 static void ActivateMagicBall(int, int);
1176
1177 struct ChangingElementInfo
1178 {
1179   int element;
1180   int target_element;
1181   int change_delay;
1182   void (*pre_change_function)(int x, int y);
1183   void (*change_function)(int x, int y);
1184   void (*post_change_function)(int x, int y);
1185 };
1186
1187 static struct ChangingElementInfo change_delay_list[] =
1188 {
1189   {
1190     EL_NUT_BREAKING,
1191     EL_EMERALD,
1192     6,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_PEARL_BREAKING,
1199     EL_EMPTY,
1200     8,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_OPENING,
1207     EL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EXIT_CLOSING,
1215     EL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_OPENING,
1223     EL_STEEL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_STEEL_EXIT_CLOSING,
1231     EL_STEEL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_OPENING,
1239     EL_EM_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_EM_EXIT_CLOSING,
1247     EL_EMPTY,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_STEEL_EXIT_OPENING,
1255     EL_EM_STEEL_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_STEEL_EXIT_CLOSING,
1263     EL_STEELWALL,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 // static variables for playfield scan mode (scanning forward or backward)
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite(void)
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars(void)
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   // make sure that stepsize value is always a power of 2
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   // do no immediately change move delay -- the player might just be moving
1625   player->move_delay_value_next = move_delay;
1626
1627   // information if player can move must be set separately
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig(void)
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660 }
1661
1662 int GetElementFromGroupElement(int element)
1663 {
1664   if (IS_GROUP_ELEMENT(element))
1665   {
1666     struct ElementGroupInfo *group = element_info[element].group;
1667     int last_anim_random_frame = gfx.anim_random_frame;
1668     int element_pos;
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = RND(group->num_elements_resolved);
1672
1673     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1674                                     group->choice_mode, 0,
1675                                     group->choice_pos);
1676
1677     if (group->choice_mode == ANIM_RANDOM)
1678       gfx.anim_random_frame = last_anim_random_frame;
1679
1680     group->choice_pos++;
1681
1682     element = group->element_resolved[element_pos];
1683   }
1684
1685   return element;
1686 }
1687
1688 static void IncrementSokobanFieldsNeeded(void)
1689 {
1690   if (level.sb_fields_needed)
1691     game.sokoban_fields_still_needed++;
1692 }
1693
1694 static void IncrementSokobanObjectsNeeded(void)
1695 {
1696   if (level.sb_objects_needed)
1697     game.sokoban_objects_still_needed++;
1698 }
1699
1700 static void DecrementSokobanFieldsNeeded(void)
1701 {
1702   if (game.sokoban_fields_still_needed > 0)
1703     game.sokoban_fields_still_needed--;
1704 }
1705
1706 static void DecrementSokobanObjectsNeeded(void)
1707 {
1708   if (game.sokoban_objects_still_needed > 0)
1709     game.sokoban_objects_still_needed--;
1710 }
1711
1712 static void InitPlayerField(int x, int y, int element, boolean init_game)
1713 {
1714   if (element == EL_SP_MURPHY)
1715   {
1716     if (init_game)
1717     {
1718       if (stored_player[0].present)
1719       {
1720         Feld[x][y] = EL_SP_MURPHY_CLONE;
1721
1722         return;
1723       }
1724       else
1725       {
1726         stored_player[0].initial_element = element;
1727         stored_player[0].use_murphy = TRUE;
1728
1729         if (!level.use_artwork_element[0])
1730           stored_player[0].artwork_element = EL_SP_MURPHY;
1731       }
1732
1733       Feld[x][y] = EL_PLAYER_1;
1734     }
1735   }
1736
1737   if (init_game)
1738   {
1739     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1740     int jx = player->jx, jy = player->jy;
1741
1742     player->present = TRUE;
1743
1744     player->block_last_field = (element == EL_SP_MURPHY ?
1745                                 level.sp_block_last_field :
1746                                 level.block_last_field);
1747
1748     // ---------- initialize player's last field block delay ------------------
1749
1750     // always start with reliable default value (no adjustment needed)
1751     player->block_delay_adjustment = 0;
1752
1753     // special case 1: in Supaplex, Murphy blocks last field one more frame
1754     if (player->block_last_field && element == EL_SP_MURPHY)
1755       player->block_delay_adjustment = 1;
1756
1757     // special case 2: in game engines before 3.1.1, blocking was different
1758     if (game.use_block_last_field_bug)
1759       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1760
1761     if (!network.enabled || player->connected_network)
1762     {
1763       player->active = TRUE;
1764
1765       // remove potentially duplicate players
1766       if (StorePlayer[jx][jy] == Feld[x][y])
1767         StorePlayer[jx][jy] = 0;
1768
1769       StorePlayer[x][y] = Feld[x][y];
1770
1771 #if DEBUG_INIT_PLAYER
1772       if (options.debug)
1773       {
1774         printf("- player element %d activated", player->element_nr);
1775         printf(" (local player is %d and currently %s)\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780 #endif
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active && player->killed)
1794       player->reanimated = TRUE; // if player was just killed, reanimate him
1795   }
1796 }
1797
1798 static void InitField(int x, int y, boolean init_game)
1799 {
1800   int element = Feld[x][y];
1801
1802   switch (element)
1803   {
1804     case EL_SP_MURPHY:
1805     case EL_PLAYER_1:
1806     case EL_PLAYER_2:
1807     case EL_PLAYER_3:
1808     case EL_PLAYER_4:
1809       InitPlayerField(x, y, element, init_game);
1810       break;
1811
1812     case EL_SOKOBAN_FIELD_PLAYER:
1813       element = Feld[x][y] = EL_PLAYER_1;
1814       InitField(x, y, init_game);
1815
1816       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1817       InitField(x, y, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_EMPTY:
1821       IncrementSokobanFieldsNeeded();
1822       break;
1823
1824     case EL_SOKOBAN_OBJECT:
1825       IncrementSokobanObjectsNeeded();
1826       break;
1827
1828     case EL_STONEBLOCK:
1829       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1839       break;
1840
1841     case EL_BUG:
1842     case EL_BUG_RIGHT:
1843     case EL_BUG_UP:
1844     case EL_BUG_LEFT:
1845     case EL_BUG_DOWN:
1846     case EL_SPACESHIP:
1847     case EL_SPACESHIP_RIGHT:
1848     case EL_SPACESHIP_UP:
1849     case EL_SPACESHIP_LEFT:
1850     case EL_SPACESHIP_DOWN:
1851     case EL_BD_BUTTERFLY:
1852     case EL_BD_BUTTERFLY_RIGHT:
1853     case EL_BD_BUTTERFLY_UP:
1854     case EL_BD_BUTTERFLY_LEFT:
1855     case EL_BD_BUTTERFLY_DOWN:
1856     case EL_BD_FIREFLY:
1857     case EL_BD_FIREFLY_RIGHT:
1858     case EL_BD_FIREFLY_UP:
1859     case EL_BD_FIREFLY_LEFT:
1860     case EL_BD_FIREFLY_DOWN:
1861     case EL_PACMAN_RIGHT:
1862     case EL_PACMAN_UP:
1863     case EL_PACMAN_LEFT:
1864     case EL_PACMAN_DOWN:
1865     case EL_YAMYAM:
1866     case EL_YAMYAM_LEFT:
1867     case EL_YAMYAM_RIGHT:
1868     case EL_YAMYAM_UP:
1869     case EL_YAMYAM_DOWN:
1870     case EL_DARK_YAMYAM:
1871     case EL_ROBOT:
1872     case EL_PACMAN:
1873     case EL_SP_SNIKSNAK:
1874     case EL_SP_ELECTRON:
1875     case EL_MOLE:
1876     case EL_MOLE_LEFT:
1877     case EL_MOLE_RIGHT:
1878     case EL_MOLE_UP:
1879     case EL_MOLE_DOWN:
1880       InitMovDir(x, y);
1881       break;
1882
1883     case EL_AMOEBA_FULL:
1884     case EL_BD_AMOEBA:
1885       InitAmoebaNr(x, y);
1886       break;
1887
1888     case EL_AMOEBA_DROP:
1889       if (y == lev_fieldy - 1)
1890       {
1891         Feld[x][y] = EL_AMOEBA_GROWING;
1892         Store[x][y] = EL_AMOEBA_WET;
1893       }
1894       break;
1895
1896     case EL_DYNAMITE_ACTIVE:
1897     case EL_SP_DISK_RED_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902       MovDelay[x][y] = 96;
1903       break;
1904
1905     case EL_EM_DYNAMITE_ACTIVE:
1906       MovDelay[x][y] = 32;
1907       break;
1908
1909     case EL_LAMP:
1910       game.lights_still_needed++;
1911       break;
1912
1913     case EL_PENGUIN:
1914       game.friends_still_needed++;
1915       break;
1916
1917     case EL_PIG:
1918     case EL_DRAGON:
1919       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920       break;
1921
1922     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934       if (init_game)
1935       {
1936         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1939
1940         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1941         {
1942           game.belt_dir[belt_nr] = belt_dir;
1943           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1944         }
1945         else    // more than one switch -- set it like the first switch
1946         {
1947           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1948         }
1949       }
1950       break;
1951
1952     case EL_LIGHT_SWITCH_ACTIVE:
1953       if (init_game)
1954         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1955       break;
1956
1957     case EL_INVISIBLE_STEELWALL:
1958     case EL_INVISIBLE_WALL:
1959     case EL_INVISIBLE_SAND:
1960       if (game.light_time_left > 0 ||
1961           game.lenses_time_left > 0)
1962         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1963       break;
1964
1965     case EL_EMC_MAGIC_BALL:
1966       if (game.ball_state)
1967         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1968       break;
1969
1970     case EL_EMC_MAGIC_BALL_SWITCH:
1971       if (game.ball_state)
1972         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1973       break;
1974
1975     case EL_TRIGGER_PLAYER:
1976     case EL_TRIGGER_ELEMENT:
1977     case EL_TRIGGER_CE_VALUE:
1978     case EL_TRIGGER_CE_SCORE:
1979     case EL_SELF:
1980     case EL_ANY_ELEMENT:
1981     case EL_CURRENT_CE_VALUE:
1982     case EL_CURRENT_CE_SCORE:
1983     case EL_PREV_CE_1:
1984     case EL_PREV_CE_2:
1985     case EL_PREV_CE_3:
1986     case EL_PREV_CE_4:
1987     case EL_PREV_CE_5:
1988     case EL_PREV_CE_6:
1989     case EL_PREV_CE_7:
1990     case EL_PREV_CE_8:
1991     case EL_NEXT_CE_1:
1992     case EL_NEXT_CE_2:
1993     case EL_NEXT_CE_3:
1994     case EL_NEXT_CE_4:
1995     case EL_NEXT_CE_5:
1996     case EL_NEXT_CE_6:
1997     case EL_NEXT_CE_7:
1998     case EL_NEXT_CE_8:
1999       // reference elements should not be used on the playfield
2000       Feld[x][y] = EL_EMPTY;
2001       break;
2002
2003     default:
2004       if (IS_CUSTOM_ELEMENT(element))
2005       {
2006         if (CAN_MOVE(element))
2007           InitMovDir(x, y);
2008
2009         if (!element_info[element].use_last_ce_value || init_game)
2010           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2011       }
2012       else if (IS_GROUP_ELEMENT(element))
2013       {
2014         Feld[x][y] = GetElementFromGroupElement(element);
2015
2016         InitField(x, y, init_game);
2017       }
2018
2019       break;
2020   }
2021
2022   if (!init_game)
2023     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2024 }
2025
2026 static void InitField_WithBug1(int x, int y, boolean init_game)
2027 {
2028   InitField(x, y, init_game);
2029
2030   // not needed to call InitMovDir() -- already done by InitField()!
2031   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2032       CAN_MOVE(Feld[x][y]))
2033     InitMovDir(x, y);
2034 }
2035
2036 static void InitField_WithBug2(int x, int y, boolean init_game)
2037 {
2038   int old_element = Feld[x][y];
2039
2040   InitField(x, y, init_game);
2041
2042   // not needed to call InitMovDir() -- already done by InitField()!
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(old_element) &&
2045       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2046     InitMovDir(x, y);
2047
2048   /* this case is in fact a combination of not less than three bugs:
2049      first, it calls InitMovDir() for elements that can move, although this is
2050      already done by InitField(); then, it checks the element that was at this
2051      field _before_ the call to InitField() (which can change it); lastly, it
2052      was not called for "mole with direction" elements, which were treated as
2053      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2054   */
2055 }
2056
2057 static int get_key_element_from_nr(int key_nr)
2058 {
2059   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2060                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2061                           EL_EM_KEY_1 : EL_KEY_1);
2062
2063   return key_base_element + key_nr;
2064 }
2065
2066 static int get_next_dropped_element(struct PlayerInfo *player)
2067 {
2068   return (player->inventory_size > 0 ?
2069           player->inventory_element[player->inventory_size - 1] :
2070           player->inventory_infinite_element != EL_UNDEFINED ?
2071           player->inventory_infinite_element :
2072           player->dynabombs_left > 0 ?
2073           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2074           EL_UNDEFINED);
2075 }
2076
2077 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2078 {
2079   // pos >= 0: get element from bottom of the stack;
2080   // pos <  0: get element from top of the stack
2081
2082   if (pos < 0)
2083   {
2084     int min_inventory_size = -pos;
2085     int inventory_pos = player->inventory_size - min_inventory_size;
2086     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2087
2088     return (player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             player->inventory_infinite_element != EL_UNDEFINED ?
2091             player->inventory_infinite_element :
2092             player->dynabombs_left >= min_dynabombs_left ?
2093             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2094             EL_UNDEFINED);
2095   }
2096   else
2097   {
2098     int min_dynabombs_left = pos + 1;
2099     int min_inventory_size = pos + 1 - player->dynabombs_left;
2100     int inventory_pos = pos - player->dynabombs_left;
2101
2102     return (player->inventory_infinite_element != EL_UNDEFINED ?
2103             player->inventory_infinite_element :
2104             player->dynabombs_left >= min_dynabombs_left ?
2105             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2106             player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             EL_UNDEFINED);
2109   }
2110 }
2111
2112 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2113 {
2114   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2115   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2116   int compare_result;
2117
2118   if (gpo1->sort_priority != gpo2->sort_priority)
2119     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2120   else
2121     compare_result = gpo1->nr - gpo2->nr;
2122
2123   return compare_result;
2124 }
2125
2126 int getPlayerInventorySize(int player_nr)
2127 {
2128   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2129     return level.native_em_level->ply[player_nr]->dynamite;
2130   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2131     return level.native_sp_level->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               level.native_em_level->lev->time :
2216               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2217               level.native_sp_level->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                level.native_em_level->lev->score :
2225                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2226                level.native_sp_level->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               level.native_em_level->lev->required :
2232               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233               level.native_sp_level->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                      level.native_em_level->lev->required > 0 :
2239                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240                      level.native_sp_level->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 (level.native_em_level->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 (level.native_em_level->ply[player_nr]->keys & (1 << k))
2309           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2310             get_key_element_from_nr(k);
2311       }
2312       else if (stored_player[player_nr].key[k])
2313         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2314           get_key_element_from_nr(k);
2315     }
2316
2317     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318       getPlayerInventorySize(player_nr);
2319
2320     if (stored_player[player_nr].num_white_keys > 0)
2321       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2322
2323     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2324       stored_player[player_nr].num_white_keys;
2325   }
2326
2327   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2330       get_inventory_element_from_pos(local_player, i);
2331     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2332       get_inventory_element_from_pos(local_player, -i - 1);
2333   }
2334
2335   game_panel_controls[GAME_PANEL_SCORE].value = score;
2336   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2337
2338   game_panel_controls[GAME_PANEL_TIME].value = time;
2339
2340   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2341   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2342   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2343
2344   if (level.time == 0)
2345     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2346   else
2347     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2348
2349   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2350   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2351
2352   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2353
2354   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2355     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2356      EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2358     local_player->shield_normal_time_left;
2359   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2360     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2361      EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2363     local_player->shield_deadly_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EXIT].value =
2366     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2367
2368   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2369     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2370   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2371     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2372      EL_EMC_MAGIC_BALL_SWITCH);
2373
2374   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2375     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2376   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2377     game.light_time_left;
2378
2379   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2380     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2381   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2382     game.timegate_time_left;
2383
2384   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2385     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2386
2387   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2388     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2389   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2390     game.lenses_time_left;
2391
2392   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2393     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2394   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2395     game.magnify_time_left;
2396
2397   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2398     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2399      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2400      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2401      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2402      EL_BALLOON_SWITCH_NONE);
2403
2404   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2405     local_player->dynabomb_count;
2406   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2407     local_player->dynabomb_size;
2408   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2409     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2410
2411   game_panel_controls[GAME_PANEL_PENGUINS].value =
2412     game.friends_still_needed;
2413
2414   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2415     game.sokoban_objects_still_needed;
2416   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2417     game.sokoban_fields_still_needed;
2418
2419   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2420     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2421
2422   for (i = 0; i < NUM_BELTS; i++)
2423   {
2424     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2425       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2426        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2427     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2428       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2429   }
2430
2431   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2432     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2433   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2434     game.magic_wall_time_left;
2435
2436   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2437     local_player->gravity;
2438
2439   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2441
2442   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445        game.panel.element[i].id : EL_UNDEFINED);
2446
2447   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450        element_info[game.panel.element_count[i].id].element_count : 0);
2451
2452   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455        element_info[game.panel.ce_score[i].id].collect_score : 0);
2456
2457   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460        element_info[game.panel.ce_score_element[i].id].collect_score :
2461        EL_UNDEFINED);
2462
2463   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2466
2467   // update game panel control frames
2468
2469   for (i = 0; game_panel_controls[i].nr != -1; i++)
2470   {
2471     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2472
2473     if (gpc->type == TYPE_ELEMENT)
2474     {
2475       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2476       {
2477         int last_anim_random_frame = gfx.anim_random_frame;
2478         int element = gpc->value;
2479         int graphic = el2panelimg(element);
2480
2481         if (gpc->value != gpc->last_value)
2482         {
2483           gpc->gfx_frame = 0;
2484           gpc->gfx_random = INIT_GFX_RANDOM();
2485         }
2486         else
2487         {
2488           gpc->gfx_frame++;
2489
2490           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492             gpc->gfx_random = INIT_GFX_RANDOM();
2493         }
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = gpc->gfx_random;
2497
2498         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499           gpc->gfx_frame = element_info[element].collect_score;
2500
2501         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2502                                               gpc->gfx_frame);
2503
2504         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505           gfx.anim_random_frame = last_anim_random_frame;
2506       }
2507     }
2508     else if (gpc->type == TYPE_GRAPHIC)
2509     {
2510       if (gpc->graphic != IMG_UNDEFINED)
2511       {
2512         int last_anim_random_frame = gfx.anim_random_frame;
2513         int graphic = gpc->graphic;
2514
2515         if (gpc->value != gpc->last_value)
2516         {
2517           gpc->gfx_frame = 0;
2518           gpc->gfx_random = INIT_GFX_RANDOM();
2519         }
2520         else
2521         {
2522           gpc->gfx_frame++;
2523
2524           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2525               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2526             gpc->gfx_random = INIT_GFX_RANDOM();
2527         }
2528
2529         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2530           gfx.anim_random_frame = gpc->gfx_random;
2531
2532         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2533
2534         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2535           gfx.anim_random_frame = last_anim_random_frame;
2536       }
2537     }
2538   }
2539 }
2540
2541 static void DisplayGameControlValues(void)
2542 {
2543   boolean redraw_panel = FALSE;
2544   int i;
2545
2546   for (i = 0; game_panel_controls[i].nr != -1; i++)
2547   {
2548     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2549
2550     if (PANEL_DEACTIVATED(gpc->pos))
2551       continue;
2552
2553     if (gpc->value == gpc->last_value &&
2554         gpc->frame == gpc->last_frame)
2555       continue;
2556
2557     redraw_panel = TRUE;
2558   }
2559
2560   if (!redraw_panel)
2561     return;
2562
2563   // copy default game door content to main double buffer
2564
2565   // !!! CHECK AGAIN !!!
2566   SetPanelBackground();
2567   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2568   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2569
2570   // redraw game control buttons
2571   RedrawGameButtons();
2572
2573   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2574
2575   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2576   {
2577     int nr = game_panel_order[i].nr;
2578     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2579     struct TextPosInfo *pos = gpc->pos;
2580     int type = gpc->type;
2581     int value = gpc->value;
2582     int frame = gpc->frame;
2583     int size = pos->size;
2584     int font = pos->font;
2585     boolean draw_masked = pos->draw_masked;
2586     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2587
2588     if (PANEL_DEACTIVATED(pos))
2589       continue;
2590
2591     gpc->last_value = value;
2592     gpc->last_frame = frame;
2593
2594     if (type == TYPE_INTEGER)
2595     {
2596       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2597           nr == GAME_PANEL_TIME)
2598       {
2599         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2600
2601         if (use_dynamic_size)           // use dynamic number of digits
2602         {
2603           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2604           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2605           int size2 = size1 + 1;
2606           int font1 = pos->font;
2607           int font2 = pos->font_alt;
2608
2609           size = (value < value_change ? size1 : size2);
2610           font = (value < value_change ? font1 : font2);
2611         }
2612       }
2613
2614       // correct text size if "digits" is zero or less
2615       if (size <= 0)
2616         size = strlen(int2str(value, size));
2617
2618       // dynamically correct text alignment
2619       pos->width = size * getFontWidth(font);
2620
2621       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2622                   int2str(value, size), font, mask_mode);
2623     }
2624     else if (type == TYPE_ELEMENT)
2625     {
2626       int element, graphic;
2627       Bitmap *src_bitmap;
2628       int src_x, src_y;
2629       int width, height;
2630       int dst_x = PANEL_XPOS(pos);
2631       int dst_y = PANEL_YPOS(pos);
2632
2633       if (value != EL_UNDEFINED && value != EL_EMPTY)
2634       {
2635         element = value;
2636         graphic = el2panelimg(value);
2637
2638         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2639
2640         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2641           size = TILESIZE;
2642
2643         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2644                               &src_x, &src_y);
2645
2646         width  = graphic_info[graphic].width  * size / TILESIZE;
2647         height = graphic_info[graphic].height * size / TILESIZE;
2648
2649         if (draw_masked)
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         else
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655       }
2656     }
2657     else if (type == TYPE_GRAPHIC)
2658     {
2659       int graphic        = gpc->graphic;
2660       int graphic_active = gpc->graphic_active;
2661       Bitmap *src_bitmap;
2662       int src_x, src_y;
2663       int width, height;
2664       int dst_x = PANEL_XPOS(pos);
2665       int dst_y = PANEL_YPOS(pos);
2666       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2667                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2668
2669       if (graphic != IMG_UNDEFINED && !skip)
2670       {
2671         if (pos->style == STYLE_REVERSE)
2672           value = 100 - value;
2673
2674         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           width  = graphic_info[graphic_active].width * value / 100;
2679           height = graphic_info[graphic_active].height;
2680
2681           if (pos->direction == MV_LEFT)
2682           {
2683             src_x += graphic_info[graphic_active].width - width;
2684             dst_x += graphic_info[graphic_active].width - width;
2685           }
2686         }
2687         else
2688         {
2689           width  = graphic_info[graphic_active].width;
2690           height = graphic_info[graphic_active].height * value / 100;
2691
2692           if (pos->direction == MV_UP)
2693           {
2694             src_y += graphic_info[graphic_active].height - height;
2695             dst_y += graphic_info[graphic_active].height - height;
2696           }
2697         }
2698
2699         if (draw_masked)
2700           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2701                            dst_x, dst_y);
2702         else
2703           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2704                      dst_x, dst_y);
2705
2706         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2707
2708         if (pos->direction & MV_HORIZONTAL)
2709         {
2710           if (pos->direction == MV_RIGHT)
2711           {
2712             src_x += width;
2713             dst_x += width;
2714           }
2715           else
2716           {
2717             dst_x = PANEL_XPOS(pos);
2718           }
2719
2720           width = graphic_info[graphic].width - width;
2721         }
2722         else
2723         {
2724           if (pos->direction == MV_DOWN)
2725           {
2726             src_y += height;
2727             dst_y += height;
2728           }
2729           else
2730           {
2731             dst_y = PANEL_YPOS(pos);
2732           }
2733
2734           height = graphic_info[graphic].height - height;
2735         }
2736
2737         if (draw_masked)
2738           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2739                            dst_x, dst_y);
2740         else
2741           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2742                      dst_x, dst_y);
2743       }
2744     }
2745     else if (type == TYPE_STRING)
2746     {
2747       boolean active = (value != 0);
2748       char *state_normal = "off";
2749       char *state_active = "on";
2750       char *state = (active ? state_active : state_normal);
2751       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2752                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2753                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2754                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2755
2756       if (nr == GAME_PANEL_GRAVITY_STATE)
2757       {
2758         int font1 = pos->font;          // (used for normal state)
2759         int font2 = pos->font_alt;      // (used for active state)
2760
2761         font = (active ? font2 : font1);
2762       }
2763
2764       if (s != NULL)
2765       {
2766         char *s_cut;
2767
2768         if (size <= 0)
2769         {
2770           // don't truncate output if "chars" is zero or less
2771           size = strlen(s);
2772
2773           // dynamically correct text alignment
2774           pos->width = size * getFontWidth(font);
2775         }
2776
2777         s_cut = getStringCopyN(s, size);
2778
2779         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2780                     s_cut, font, mask_mode);
2781
2782         free(s_cut);
2783       }
2784     }
2785
2786     redraw_mask |= REDRAW_DOOR_1;
2787   }
2788
2789   SetGameStatus(GAME_MODE_PLAYING);
2790 }
2791
2792 void UpdateAndDisplayGameControlValues(void)
2793 {
2794   if (tape.deactivate_display)
2795     return;
2796
2797   UpdateGameControlValues();
2798   DisplayGameControlValues();
2799 }
2800
2801 #if 0
2802 static void UpdateGameDoorValues(void)
2803 {
2804   UpdateGameControlValues();
2805 }
2806 #endif
2807
2808 void DrawGameDoorValues(void)
2809 {
2810   DisplayGameControlValues();
2811 }
2812
2813
2814 // ============================================================================
2815 // InitGameEngine()
2816 // ----------------------------------------------------------------------------
2817 // initialize game engine due to level / tape version number
2818 // ============================================================================
2819
2820 static void InitGameEngine(void)
2821 {
2822   int i, j, k, l, x, y;
2823
2824   // set game engine from tape file when re-playing, else from level file
2825   game.engine_version = (tape.playing ? tape.engine_version :
2826                          level.game_version);
2827
2828   // set single or multi-player game mode (needed for re-playing tapes)
2829   game.team_mode = setup.team_mode;
2830
2831   if (tape.playing)
2832   {
2833     int num_players = 0;
2834
2835     for (i = 0; i < MAX_PLAYERS; i++)
2836       if (tape.player_participates[i])
2837         num_players++;
2838
2839     // multi-player tapes contain input data for more than one player
2840     game.team_mode = (num_players > 1);
2841   }
2842
2843   // --------------------------------------------------------------------------
2844   // set flags for bugs and changes according to active game engine version
2845   // --------------------------------------------------------------------------
2846
2847   /*
2848     Summary of bugfix/change:
2849     Fixed handling for custom elements that change when pushed by the player.
2850
2851     Fixed/changed in version:
2852     3.1.0
2853
2854     Description:
2855     Before 3.1.0, custom elements that "change when pushing" changed directly
2856     after the player started pushing them (until then handled in "DigField()").
2857     Since 3.1.0, these custom elements are not changed until the "pushing"
2858     move of the element is finished (now handled in "ContinueMoving()").
2859
2860     Affected levels/tapes:
2861     The first condition is generally needed for all levels/tapes before version
2862     3.1.0, which might use the old behaviour before it was changed; known tapes
2863     that are affected are some tapes from the level set "Walpurgis Gardens" by
2864     Jamie Cullen.
2865     The second condition is an exception from the above case and is needed for
2866     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2867     above (including some development versions of 3.1.0), but before it was
2868     known that this change would break tapes like the above and was fixed in
2869     3.1.1, so that the changed behaviour was active although the engine version
2870     while recording maybe was before 3.1.0. There is at least one tape that is
2871     affected by this exception, which is the tape for the one-level set "Bug
2872     Machine" by Juergen Bonhagen.
2873   */
2874
2875   game.use_change_when_pushing_bug =
2876     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2877      !(tape.playing &&
2878        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2879        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2880
2881   /*
2882     Summary of bugfix/change:
2883     Fixed handling for blocking the field the player leaves when moving.
2884
2885     Fixed/changed in version:
2886     3.1.1
2887
2888     Description:
2889     Before 3.1.1, when "block last field when moving" was enabled, the field
2890     the player is leaving when moving was blocked for the time of the move,
2891     and was directly unblocked afterwards. This resulted in the last field
2892     being blocked for exactly one less than the number of frames of one player
2893     move. Additionally, even when blocking was disabled, the last field was
2894     blocked for exactly one frame.
2895     Since 3.1.1, due to changes in player movement handling, the last field
2896     is not blocked at all when blocking is disabled. When blocking is enabled,
2897     the last field is blocked for exactly the number of frames of one player
2898     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2899     last field is blocked for exactly one more than the number of frames of
2900     one player move.
2901
2902     Affected levels/tapes:
2903     (!!! yet to be determined -- probably many !!!)
2904   */
2905
2906   game.use_block_last_field_bug =
2907     (game.engine_version < VERSION_IDENT(3,1,1,0));
2908
2909   game_em.use_single_button =
2910     (game.engine_version > VERSION_IDENT(4,0,0,2));
2911
2912   game_em.use_snap_key_bug =
2913     (game.engine_version < VERSION_IDENT(4,0,1,0));
2914
2915   // --------------------------------------------------------------------------
2916
2917   // set maximal allowed number of custom element changes per game frame
2918   game.max_num_changes_per_frame = 1;
2919
2920   // default scan direction: scan playfield from top/left to bottom/right
2921   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2922
2923   // dynamically adjust element properties according to game engine version
2924   InitElementPropertiesEngine(game.engine_version);
2925
2926 #if 0
2927   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2928   printf("          tape version == %06d [%s] [file: %06d]\n",
2929          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2930          tape.file_version);
2931   printf("       => game.engine_version == %06d\n", game.engine_version);
2932 #endif
2933
2934   // ---------- initialize player's initial move delay ------------------------
2935
2936   // dynamically adjust player properties according to level information
2937   for (i = 0; i < MAX_PLAYERS; i++)
2938     game.initial_move_delay_value[i] =
2939       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2940
2941   // dynamically adjust player properties according to game engine version
2942   for (i = 0; i < MAX_PLAYERS; i++)
2943     game.initial_move_delay[i] =
2944       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2945        game.initial_move_delay_value[i] : 0);
2946
2947   // ---------- initialize player's initial push delay ------------------------
2948
2949   // dynamically adjust player properties according to game engine version
2950   game.initial_push_delay_value =
2951     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2952
2953   // ---------- initialize changing elements ----------------------------------
2954
2955   // initialize changing elements information
2956   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2957   {
2958     struct ElementInfo *ei = &element_info[i];
2959
2960     // this pointer might have been changed in the level editor
2961     ei->change = &ei->change_page[0];
2962
2963     if (!IS_CUSTOM_ELEMENT(i))
2964     {
2965       ei->change->target_element = EL_EMPTY_SPACE;
2966       ei->change->delay_fixed = 0;
2967       ei->change->delay_random = 0;
2968       ei->change->delay_frames = 1;
2969     }
2970
2971     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2972     {
2973       ei->has_change_event[j] = FALSE;
2974
2975       ei->event_page_nr[j] = 0;
2976       ei->event_page[j] = &ei->change_page[0];
2977     }
2978   }
2979
2980   // add changing elements from pre-defined list
2981   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2982   {
2983     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2984     struct ElementInfo *ei = &element_info[ch_delay->element];
2985
2986     ei->change->target_element       = ch_delay->target_element;
2987     ei->change->delay_fixed          = ch_delay->change_delay;
2988
2989     ei->change->pre_change_function  = ch_delay->pre_change_function;
2990     ei->change->change_function      = ch_delay->change_function;
2991     ei->change->post_change_function = ch_delay->post_change_function;
2992
2993     ei->change->can_change = TRUE;
2994     ei->change->can_change_or_has_action = TRUE;
2995
2996     ei->has_change_event[CE_DELAY] = TRUE;
2997
2998     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2999     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3000   }
3001
3002   // ---------- initialize internal run-time variables ------------------------
3003
3004   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3005   {
3006     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3007
3008     for (j = 0; j < ei->num_change_pages; j++)
3009     {
3010       ei->change_page[j].can_change_or_has_action =
3011         (ei->change_page[j].can_change |
3012          ei->change_page[j].has_action);
3013     }
3014   }
3015
3016   // add change events from custom element configuration
3017   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3018   {
3019     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3020
3021     for (j = 0; j < ei->num_change_pages; j++)
3022     {
3023       if (!ei->change_page[j].can_change_or_has_action)
3024         continue;
3025
3026       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3027       {
3028         // only add event page for the first page found with this event
3029         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3030         {
3031           ei->has_change_event[k] = TRUE;
3032
3033           ei->event_page_nr[k] = j;
3034           ei->event_page[k] = &ei->change_page[j];
3035         }
3036       }
3037     }
3038   }
3039
3040   // ---------- initialize reference elements in change conditions ------------
3041
3042   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3043   {
3044     int element = EL_CUSTOM_START + i;
3045     struct ElementInfo *ei = &element_info[element];
3046
3047     for (j = 0; j < ei->num_change_pages; j++)
3048     {
3049       int trigger_element = ei->change_page[j].initial_trigger_element;
3050
3051       if (trigger_element >= EL_PREV_CE_8 &&
3052           trigger_element <= EL_NEXT_CE_8)
3053         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3054
3055       ei->change_page[j].trigger_element = trigger_element;
3056     }
3057   }
3058
3059   // ---------- initialize run-time trigger player and element ----------------
3060
3061   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3062   {
3063     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3064
3065     for (j = 0; j < ei->num_change_pages; j++)
3066     {
3067       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3068       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3069       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3070       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3071       ei->change_page[j].actual_trigger_ce_value = 0;
3072       ei->change_page[j].actual_trigger_ce_score = 0;
3073     }
3074   }
3075
3076   // ---------- initialize trigger events -------------------------------------
3077
3078   // initialize trigger events information
3079   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3080     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3081       trigger_events[i][j] = FALSE;
3082
3083   // add trigger events from element change event properties
3084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3085   {
3086     struct ElementInfo *ei = &element_info[i];
3087
3088     for (j = 0; j < ei->num_change_pages; j++)
3089     {
3090       if (!ei->change_page[j].can_change_or_has_action)
3091         continue;
3092
3093       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3094       {
3095         int trigger_element = ei->change_page[j].trigger_element;
3096
3097         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3098         {
3099           if (ei->change_page[j].has_event[k])
3100           {
3101             if (IS_GROUP_ELEMENT(trigger_element))
3102             {
3103               struct ElementGroupInfo *group =
3104                 element_info[trigger_element].group;
3105
3106               for (l = 0; l < group->num_elements_resolved; l++)
3107                 trigger_events[group->element_resolved[l]][k] = TRUE;
3108             }
3109             else if (trigger_element == EL_ANY_ELEMENT)
3110               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3111                 trigger_events[l][k] = TRUE;
3112             else
3113               trigger_events[trigger_element][k] = TRUE;
3114           }
3115         }
3116       }
3117     }
3118   }
3119
3120   // ---------- initialize push delay -----------------------------------------
3121
3122   // initialize push delay values to default
3123   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3124   {
3125     if (!IS_CUSTOM_ELEMENT(i))
3126     {
3127       // set default push delay values (corrected since version 3.0.7-1)
3128       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3129       {
3130         element_info[i].push_delay_fixed = 2;
3131         element_info[i].push_delay_random = 8;
3132       }
3133       else
3134       {
3135         element_info[i].push_delay_fixed = 8;
3136         element_info[i].push_delay_random = 8;
3137       }
3138     }
3139   }
3140
3141   // set push delay value for certain elements from pre-defined list
3142   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3143   {
3144     int e = push_delay_list[i].element;
3145
3146     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3147     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3148   }
3149
3150   // set push delay value for Supaplex elements for newer engine versions
3151   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3152   {
3153     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3154     {
3155       if (IS_SP_ELEMENT(i))
3156       {
3157         // set SP push delay to just enough to push under a falling zonk
3158         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3159
3160         element_info[i].push_delay_fixed  = delay;
3161         element_info[i].push_delay_random = 0;
3162       }
3163     }
3164   }
3165
3166   // ---------- initialize move stepsize --------------------------------------
3167
3168   // initialize move stepsize values to default
3169   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3170     if (!IS_CUSTOM_ELEMENT(i))
3171       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3172
3173   // set move stepsize value for certain elements from pre-defined list
3174   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3175   {
3176     int e = move_stepsize_list[i].element;
3177
3178     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3179   }
3180
3181   // ---------- initialize collect score --------------------------------------
3182
3183   // initialize collect score values for custom elements from initial value
3184   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3185     if (IS_CUSTOM_ELEMENT(i))
3186       element_info[i].collect_score = element_info[i].collect_score_initial;
3187
3188   // ---------- initialize collect count --------------------------------------
3189
3190   // initialize collect count values for non-custom elements
3191   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3192     if (!IS_CUSTOM_ELEMENT(i))
3193       element_info[i].collect_count_initial = 0;
3194
3195   // add collect count values for all elements from pre-defined list
3196   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3197     element_info[collect_count_list[i].element].collect_count_initial =
3198       collect_count_list[i].count;
3199
3200   // ---------- initialize access direction -----------------------------------
3201
3202   // initialize access direction values to default (access from every side)
3203   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3204     if (!IS_CUSTOM_ELEMENT(i))
3205       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3206
3207   // set access direction value for certain elements from pre-defined list
3208   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3209     element_info[access_direction_list[i].element].access_direction =
3210       access_direction_list[i].direction;
3211
3212   // ---------- initialize explosion content ----------------------------------
3213   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3214   {
3215     if (IS_CUSTOM_ELEMENT(i))
3216       continue;
3217
3218     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3219     {
3220       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3221
3222       element_info[i].content.e[x][y] =
3223         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3224          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3225          i == EL_PLAYER_3 ? EL_EMERALD :
3226          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3227          i == EL_MOLE ? EL_EMERALD_RED :
3228          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3229          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3230          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3231          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3232          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3233          i == EL_WALL_EMERALD ? EL_EMERALD :
3234          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3235          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3236          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3237          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3238          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3239          i == EL_WALL_PEARL ? EL_PEARL :
3240          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3241          EL_EMPTY);
3242     }
3243   }
3244
3245   // ---------- initialize recursion detection --------------------------------
3246   recursion_loop_depth = 0;
3247   recursion_loop_detected = FALSE;
3248   recursion_loop_element = EL_UNDEFINED;
3249
3250   // ---------- initialize graphics engine ------------------------------------
3251   game.scroll_delay_value =
3252     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3253      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3254   game.scroll_delay_value =
3255     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3256
3257   // ---------- initialize game engine snapshots ------------------------------
3258   for (i = 0; i < MAX_PLAYERS; i++)
3259     game.snapshot.last_action[i] = 0;
3260   game.snapshot.changed_action = FALSE;
3261   game.snapshot.collected_item = FALSE;
3262   game.snapshot.mode =
3263     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3264      SNAPSHOT_MODE_EVERY_STEP :
3265      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3266      SNAPSHOT_MODE_EVERY_MOVE :
3267      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3268      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3269   game.snapshot.save_snapshot = FALSE;
3270
3271   // ---------- initialize level time for Supaplex engine ---------------------
3272   // Supaplex levels with time limit currently unsupported -- should be added
3273   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3274     level.time = 0;
3275 }
3276
3277 static int get_num_special_action(int element, int action_first,
3278                                   int action_last)
3279 {
3280   int num_special_action = 0;
3281   int i, j;
3282
3283   for (i = action_first; i <= action_last; i++)
3284   {
3285     boolean found = FALSE;
3286
3287     for (j = 0; j < NUM_DIRECTIONS; j++)
3288       if (el_act_dir2img(element, i, j) !=
3289           el_act_dir2img(element, ACTION_DEFAULT, j))
3290         found = TRUE;
3291
3292     if (found)
3293       num_special_action++;
3294     else
3295       break;
3296   }
3297
3298   return num_special_action;
3299 }
3300
3301
3302 // ============================================================================
3303 // InitGame()
3304 // ----------------------------------------------------------------------------
3305 // initialize and start new game
3306 // ============================================================================
3307
3308 #if DEBUG_INIT_PLAYER
3309 static void DebugPrintPlayerStatus(char *message)
3310 {
3311   int i;
3312
3313   if (!options.debug)
3314     return;
3315
3316   printf("%s:\n", message);
3317
3318   for (i = 0; i < MAX_PLAYERS; i++)
3319   {
3320     struct PlayerInfo *player = &stored_player[i];
3321
3322     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3323            i + 1,
3324            player->present,
3325            player->connected,
3326            player->connected_locally,
3327            player->connected_network,
3328            player->active);
3329
3330     if (local_player == player)
3331       printf(" (local player)");
3332
3333     printf("\n");
3334   }
3335 }
3336 #endif
3337
3338 void InitGame(void)
3339 {
3340   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3341   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3342   int fade_mask = REDRAW_FIELD;
3343
3344   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3345   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3346   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3347   int initial_move_dir = MV_DOWN;
3348   int i, j, x, y;
3349
3350   // required here to update video display before fading (FIX THIS)
3351   DrawMaskedBorder(REDRAW_DOOR_2);
3352
3353   if (!game.restart_level)
3354     CloseDoor(DOOR_CLOSE_1);
3355
3356   SetGameStatus(GAME_MODE_PLAYING);
3357
3358   if (level_editor_test_game)
3359     FadeSkipNextFadeOut();
3360   else
3361     FadeSetEnterScreen();
3362
3363   if (CheckFadeAll())
3364     fade_mask = REDRAW_ALL;
3365
3366   FadeLevelSoundsAndMusic();
3367
3368   ExpireSoundLoops(TRUE);
3369
3370   FadeOut(fade_mask);
3371
3372   if (level_editor_test_game)
3373     FadeSkipNextFadeIn();
3374
3375   // needed if different viewport properties defined for playing
3376   ChangeViewportPropertiesIfNeeded();
3377
3378   ClearField();
3379
3380   DrawCompleteVideoDisplay();
3381
3382   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3383
3384   InitGameEngine();
3385   InitGameControlValues();
3386
3387   // don't play tapes over network
3388   network_playing = (network.enabled && !tape.playing);
3389
3390   for (i = 0; i < MAX_PLAYERS; i++)
3391   {
3392     struct PlayerInfo *player = &stored_player[i];
3393
3394     player->index_nr = i;
3395     player->index_bit = (1 << i);
3396     player->element_nr = EL_PLAYER_1 + i;
3397
3398     player->present = FALSE;
3399     player->active = FALSE;
3400     player->mapped = FALSE;
3401
3402     player->killed = FALSE;
3403     player->reanimated = FALSE;
3404     player->buried = FALSE;
3405
3406     player->action = 0;
3407     player->effective_action = 0;
3408     player->programmed_action = 0;
3409     player->snap_action = 0;
3410
3411     player->mouse_action.lx = 0;
3412     player->mouse_action.ly = 0;
3413     player->mouse_action.button = 0;
3414     player->mouse_action.button_hint = 0;
3415
3416     player->effective_mouse_action.lx = 0;
3417     player->effective_mouse_action.ly = 0;
3418     player->effective_mouse_action.button = 0;
3419     player->effective_mouse_action.button_hint = 0;
3420
3421     for (j = 0; j < MAX_NUM_KEYS; j++)
3422       player->key[j] = FALSE;
3423
3424     player->num_white_keys = 0;
3425
3426     player->dynabomb_count = 0;
3427     player->dynabomb_size = 1;
3428     player->dynabombs_left = 0;
3429     player->dynabomb_xl = FALSE;
3430
3431     player->MovDir = initial_move_dir;
3432     player->MovPos = 0;
3433     player->GfxPos = 0;
3434     player->GfxDir = initial_move_dir;
3435     player->GfxAction = ACTION_DEFAULT;
3436     player->Frame = 0;
3437     player->StepFrame = 0;
3438
3439     player->initial_element = player->element_nr;
3440     player->artwork_element =
3441       (level.use_artwork_element[i] ? level.artwork_element[i] :
3442        player->element_nr);
3443     player->use_murphy = FALSE;
3444
3445     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3446     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3447
3448     player->gravity = level.initial_player_gravity[i];
3449
3450     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3451
3452     player->actual_frame_counter = 0;
3453
3454     player->step_counter = 0;
3455
3456     player->last_move_dir = initial_move_dir;
3457
3458     player->is_active = FALSE;
3459
3460     player->is_waiting = FALSE;
3461     player->is_moving = FALSE;
3462     player->is_auto_moving = FALSE;
3463     player->is_digging = FALSE;
3464     player->is_snapping = FALSE;
3465     player->is_collecting = FALSE;
3466     player->is_pushing = FALSE;
3467     player->is_switching = FALSE;
3468     player->is_dropping = FALSE;
3469     player->is_dropping_pressed = FALSE;
3470
3471     player->is_bored = FALSE;
3472     player->is_sleeping = FALSE;
3473
3474     player->was_waiting = TRUE;
3475     player->was_moving = FALSE;
3476     player->was_snapping = FALSE;
3477     player->was_dropping = FALSE;
3478
3479     player->force_dropping = FALSE;
3480
3481     player->frame_counter_bored = -1;
3482     player->frame_counter_sleeping = -1;
3483
3484     player->anim_delay_counter = 0;
3485     player->post_delay_counter = 0;
3486
3487     player->dir_waiting = initial_move_dir;
3488     player->action_waiting = ACTION_DEFAULT;
3489     player->last_action_waiting = ACTION_DEFAULT;
3490     player->special_action_bored = ACTION_DEFAULT;
3491     player->special_action_sleeping = ACTION_DEFAULT;
3492
3493     player->switch_x = -1;
3494     player->switch_y = -1;
3495
3496     player->drop_x = -1;
3497     player->drop_y = -1;
3498
3499     player->show_envelope = 0;
3500
3501     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3502
3503     player->push_delay       = -1;      // initialized when pushing starts
3504     player->push_delay_value = game.initial_push_delay_value;
3505
3506     player->drop_delay = 0;
3507     player->drop_pressed_delay = 0;
3508
3509     player->last_jx = -1;
3510     player->last_jy = -1;
3511     player->jx = -1;
3512     player->jy = -1;
3513
3514     player->shield_normal_time_left = 0;
3515     player->shield_deadly_time_left = 0;
3516
3517     player->inventory_infinite_element = EL_UNDEFINED;
3518     player->inventory_size = 0;
3519
3520     if (level.use_initial_inventory[i])
3521     {
3522       for (j = 0; j < level.initial_inventory_size[i]; j++)
3523       {
3524         int element = level.initial_inventory_content[i][j];
3525         int collect_count = element_info[element].collect_count_initial;
3526         int k;
3527
3528         if (!IS_CUSTOM_ELEMENT(element))
3529           collect_count = 1;
3530
3531         if (collect_count == 0)
3532           player->inventory_infinite_element = element;
3533         else
3534           for (k = 0; k < collect_count; k++)
3535             if (player->inventory_size < MAX_INVENTORY_SIZE)
3536               player->inventory_element[player->inventory_size++] = element;
3537       }
3538     }
3539
3540     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3541     SnapField(player, 0, 0);
3542
3543     map_player_action[i] = i;
3544   }
3545
3546   network_player_action_received = FALSE;
3547
3548   // initial null action
3549   if (network_playing)
3550     SendToServer_MovePlayer(MV_NONE);
3551
3552   FrameCounter = 0;
3553   TimeFrames = 0;
3554   TimePlayed = 0;
3555   TimeLeft = level.time;
3556   TapeTime = 0;
3557
3558   ScreenMovDir = MV_NONE;
3559   ScreenMovPos = 0;
3560   ScreenGfxPos = 0;
3561
3562   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3563
3564   game.robot_wheel_x = -1;
3565   game.robot_wheel_y = -1;
3566
3567   game.exit_x = -1;
3568   game.exit_y = -1;
3569
3570   game.all_players_gone = FALSE;
3571
3572   game.LevelSolved = FALSE;
3573   game.GameOver = FALSE;
3574
3575   game.GamePlayed = !tape.playing;
3576
3577   game.LevelSolved_GameWon = FALSE;
3578   game.LevelSolved_GameEnd = FALSE;
3579   game.LevelSolved_SaveTape = FALSE;
3580   game.LevelSolved_SaveScore = FALSE;
3581
3582   game.LevelSolved_CountingTime = 0;
3583   game.LevelSolved_CountingScore = 0;
3584   game.LevelSolved_CountingHealth = 0;
3585
3586   game.panel.active = TRUE;
3587
3588   game.no_time_limit = (level.time == 0);
3589
3590   game.yamyam_content_nr = 0;
3591   game.robot_wheel_active = FALSE;
3592   game.magic_wall_active = FALSE;
3593   game.magic_wall_time_left = 0;
3594   game.light_time_left = 0;
3595   game.timegate_time_left = 0;
3596   game.switchgate_pos = 0;
3597   game.wind_direction = level.wind_direction_initial;
3598
3599   game.score = 0;
3600   game.score_final = 0;
3601
3602   game.health = MAX_HEALTH;
3603   game.health_final = MAX_HEALTH;
3604
3605   game.gems_still_needed = level.gems_needed;
3606   game.sokoban_fields_still_needed = 0;
3607   game.sokoban_objects_still_needed = 0;
3608   game.lights_still_needed = 0;
3609   game.players_still_needed = 0;
3610   game.friends_still_needed = 0;
3611
3612   game.lenses_time_left = 0;
3613   game.magnify_time_left = 0;
3614
3615   game.ball_state = level.ball_state_initial;
3616   game.ball_content_nr = 0;
3617
3618   game.explosions_delayed = TRUE;
3619
3620   game.envelope_active = FALSE;
3621
3622   for (i = 0; i < NUM_BELTS; i++)
3623   {
3624     game.belt_dir[i] = MV_NONE;
3625     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3626   }
3627
3628   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3629     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3630
3631 #if DEBUG_INIT_PLAYER
3632   DebugPrintPlayerStatus("Player status at level initialization");
3633 #endif
3634
3635   SCAN_PLAYFIELD(x, y)
3636   {
3637     Feld[x][y] = Last[x][y] = level.field[x][y];
3638     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3639     ChangeDelay[x][y] = 0;
3640     ChangePage[x][y] = -1;
3641     CustomValue[x][y] = 0;              // initialized in InitField()
3642     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3643     AmoebaNr[x][y] = 0;
3644     WasJustMoving[x][y] = 0;
3645     WasJustFalling[x][y] = 0;
3646     CheckCollision[x][y] = 0;
3647     CheckImpact[x][y] = 0;
3648     Stop[x][y] = FALSE;
3649     Pushed[x][y] = FALSE;
3650
3651     ChangeCount[x][y] = 0;
3652     ChangeEvent[x][y] = -1;
3653
3654     ExplodePhase[x][y] = 0;
3655     ExplodeDelay[x][y] = 0;
3656     ExplodeField[x][y] = EX_TYPE_NONE;
3657
3658     RunnerVisit[x][y] = 0;
3659     PlayerVisit[x][y] = 0;
3660
3661     GfxFrame[x][y] = 0;
3662     GfxRandom[x][y] = INIT_GFX_RANDOM();
3663     GfxElement[x][y] = EL_UNDEFINED;
3664     GfxAction[x][y] = ACTION_DEFAULT;
3665     GfxDir[x][y] = MV_NONE;
3666     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3667   }
3668
3669   SCAN_PLAYFIELD(x, y)
3670   {
3671     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3672       emulate_bd = FALSE;
3673     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3674       emulate_sb = FALSE;
3675     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3676       emulate_sp = FALSE;
3677
3678     InitField(x, y, TRUE);
3679
3680     ResetGfxAnimation(x, y);
3681   }
3682
3683   InitBeltMovement();
3684
3685   for (i = 0; i < MAX_PLAYERS; i++)
3686   {
3687     struct PlayerInfo *player = &stored_player[i];
3688
3689     // set number of special actions for bored and sleeping animation
3690     player->num_special_action_bored =
3691       get_num_special_action(player->artwork_element,
3692                              ACTION_BORING_1, ACTION_BORING_LAST);
3693     player->num_special_action_sleeping =
3694       get_num_special_action(player->artwork_element,
3695                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3696   }
3697
3698   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3699                     emulate_sb ? EMU_SOKOBAN :
3700                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3701
3702   // initialize type of slippery elements
3703   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3704   {
3705     if (!IS_CUSTOM_ELEMENT(i))
3706     {
3707       // default: elements slip down either to the left or right randomly
3708       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3709
3710       // SP style elements prefer to slip down on the left side
3711       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3712         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3713
3714       // BD style elements prefer to slip down on the left side
3715       if (game.emulation == EMU_BOULDERDASH)
3716         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3717     }
3718   }
3719
3720   // initialize explosion and ignition delay
3721   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3722   {
3723     if (!IS_CUSTOM_ELEMENT(i))
3724     {
3725       int num_phase = 8;
3726       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3727                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3728                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3729       int last_phase = (num_phase + 1) * delay;
3730       int half_phase = (num_phase / 2) * delay;
3731
3732       element_info[i].explosion_delay = last_phase - 1;
3733       element_info[i].ignition_delay = half_phase;
3734
3735       if (i == EL_BLACK_ORB)
3736         element_info[i].ignition_delay = 1;
3737     }
3738   }
3739
3740   // correct non-moving belts to start moving left
3741   for (i = 0; i < NUM_BELTS; i++)
3742     if (game.belt_dir[i] == MV_NONE)
3743       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3744
3745 #if USE_NEW_PLAYER_ASSIGNMENTS
3746   // use preferred player also in local single-player mode
3747   if (!network.enabled && !game.team_mode)
3748   {
3749     int old_index_nr = local_player->index_nr;
3750     int new_index_nr = setup.network_player_nr;
3751
3752     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3753     {
3754       stored_player[old_index_nr].connected_locally = FALSE;
3755       stored_player[new_index_nr].connected_locally = TRUE;
3756     }
3757   }
3758
3759   for (i = 0; i < MAX_PLAYERS; i++)
3760   {
3761     stored_player[i].connected = FALSE;
3762
3763     // in network game mode, the local player might not be the first player
3764     if (stored_player[i].connected_locally)
3765       local_player = &stored_player[i];
3766   }
3767
3768   if (!network.enabled)
3769     local_player->connected = TRUE;
3770
3771   if (tape.playing)
3772   {
3773     for (i = 0; i < MAX_PLAYERS; i++)
3774       stored_player[i].connected = tape.player_participates[i];
3775   }
3776   else if (network.enabled)
3777   {
3778     // add team mode players connected over the network (needed for correct
3779     // assignment of player figures from level to locally playing players)
3780
3781     for (i = 0; i < MAX_PLAYERS; i++)
3782       if (stored_player[i].connected_network)
3783         stored_player[i].connected = TRUE;
3784   }
3785   else if (game.team_mode)
3786   {
3787     // try to guess locally connected team mode players (needed for correct
3788     // assignment of player figures from level to locally playing players)
3789
3790     for (i = 0; i < MAX_PLAYERS; i++)
3791       if (setup.input[i].use_joystick ||
3792           setup.input[i].key.left != KSYM_UNDEFINED)
3793         stored_player[i].connected = TRUE;
3794   }
3795
3796 #if DEBUG_INIT_PLAYER
3797   DebugPrintPlayerStatus("Player status after level initialization");
3798 #endif
3799
3800 #if DEBUG_INIT_PLAYER
3801   if (options.debug)
3802     printf("Reassigning players ...\n");
3803 #endif
3804
3805   // check if any connected player was not found in playfield
3806   for (i = 0; i < MAX_PLAYERS; i++)
3807   {
3808     struct PlayerInfo *player = &stored_player[i];
3809
3810     if (player->connected && !player->present)
3811     {
3812       struct PlayerInfo *field_player = NULL;
3813
3814 #if DEBUG_INIT_PLAYER
3815       if (options.debug)
3816         printf("- looking for field player for player %d ...\n", i + 1);
3817 #endif
3818
3819       // assign first free player found that is present in the playfield
3820
3821       // first try: look for unmapped playfield player that is not connected
3822       for (j = 0; j < MAX_PLAYERS; j++)
3823         if (field_player == NULL &&
3824             stored_player[j].present &&
3825             !stored_player[j].mapped &&
3826             !stored_player[j].connected)
3827           field_player = &stored_player[j];
3828
3829       // second try: look for *any* unmapped playfield player
3830       for (j = 0; j < MAX_PLAYERS; j++)
3831         if (field_player == NULL &&
3832             stored_player[j].present &&
3833             !stored_player[j].mapped)
3834           field_player = &stored_player[j];
3835
3836       if (field_player != NULL)
3837       {
3838         int jx = field_player->jx, jy = field_player->jy;
3839
3840 #if DEBUG_INIT_PLAYER
3841         if (options.debug)
3842           printf("- found player %d\n", field_player->index_nr + 1);
3843 #endif
3844
3845         player->present = FALSE;
3846         player->active = FALSE;
3847
3848         field_player->present = TRUE;
3849         field_player->active = TRUE;
3850
3851         /*
3852         player->initial_element = field_player->initial_element;
3853         player->artwork_element = field_player->artwork_element;
3854
3855         player->block_last_field       = field_player->block_last_field;
3856         player->block_delay_adjustment = field_player->block_delay_adjustment;
3857         */
3858
3859         StorePlayer[jx][jy] = field_player->element_nr;
3860
3861         field_player->jx = field_player->last_jx = jx;
3862         field_player->jy = field_player->last_jy = jy;
3863
3864         if (local_player == player)
3865           local_player = field_player;
3866
3867         map_player_action[field_player->index_nr] = i;
3868
3869         field_player->mapped = TRUE;
3870
3871 #if DEBUG_INIT_PLAYER
3872         if (options.debug)
3873           printf("- map_player_action[%d] == %d\n",
3874                  field_player->index_nr + 1, i + 1);
3875 #endif
3876       }
3877     }
3878
3879     if (player->connected && player->present)
3880       player->mapped = TRUE;
3881   }
3882
3883 #if DEBUG_INIT_PLAYER
3884   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3885 #endif
3886
3887 #else
3888
3889   // check if any connected player was not found in playfield
3890   for (i = 0; i < MAX_PLAYERS; i++)
3891   {
3892     struct PlayerInfo *player = &stored_player[i];
3893
3894     if (player->connected && !player->present)
3895     {
3896       for (j = 0; j < MAX_PLAYERS; j++)
3897       {
3898         struct PlayerInfo *field_player = &stored_player[j];
3899         int jx = field_player->jx, jy = field_player->jy;
3900
3901         // assign first free player found that is present in the playfield
3902         if (field_player->present && !field_player->connected)
3903         {
3904           player->present = TRUE;
3905           player->active = TRUE;
3906
3907           field_player->present = FALSE;
3908           field_player->active = FALSE;
3909
3910           player->initial_element = field_player->initial_element;
3911           player->artwork_element = field_player->artwork_element;
3912
3913           player->block_last_field       = field_player->block_last_field;
3914           player->block_delay_adjustment = field_player->block_delay_adjustment;
3915
3916           StorePlayer[jx][jy] = player->element_nr;
3917
3918           player->jx = player->last_jx = jx;
3919           player->jy = player->last_jy = jy;
3920
3921           break;
3922         }
3923       }
3924     }
3925   }
3926 #endif
3927
3928 #if 0
3929   printf("::: local_player->present == %d\n", local_player->present);
3930 #endif
3931
3932   // set focus to local player for network games, else to all players
3933   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3934   game.centered_player_nr_next = game.centered_player_nr;
3935   game.set_centered_player = FALSE;
3936   game.set_centered_player_fast = FALSE;
3937
3938   if (network_playing && tape.recording)
3939   {
3940     // store client dependent player focus when recording network games
3941     tape.centered_player_nr_next = game.centered_player_nr_next;
3942     tape.set_centered_player = TRUE;
3943   }
3944
3945   if (tape.playing)
3946   {
3947     // when playing a tape, eliminate all players who do not participate
3948
3949 #if USE_NEW_PLAYER_ASSIGNMENTS
3950
3951     if (!game.team_mode)
3952     {
3953       for (i = 0; i < MAX_PLAYERS; i++)
3954       {
3955         if (stored_player[i].active &&
3956             !tape.player_participates[map_player_action[i]])
3957         {
3958           struct PlayerInfo *player = &stored_player[i];
3959           int jx = player->jx, jy = player->jy;
3960
3961 #if DEBUG_INIT_PLAYER
3962           if (options.debug)
3963             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3964 #endif
3965
3966           player->active = FALSE;
3967           StorePlayer[jx][jy] = 0;
3968           Feld[jx][jy] = EL_EMPTY;
3969         }
3970       }
3971     }
3972
3973 #else
3974
3975     for (i = 0; i < MAX_PLAYERS; i++)
3976     {
3977       if (stored_player[i].active &&
3978           !tape.player_participates[i])
3979       {
3980         struct PlayerInfo *player = &stored_player[i];
3981         int jx = player->jx, jy = player->jy;
3982
3983         player->active = FALSE;
3984         StorePlayer[jx][jy] = 0;
3985         Feld[jx][jy] = EL_EMPTY;
3986       }
3987     }
3988 #endif
3989   }
3990   else if (!network.enabled && !game.team_mode)         // && !tape.playing
3991   {
3992     // when in single player mode, eliminate all but the local player
3993
3994     for (i = 0; i < MAX_PLAYERS; i++)
3995     {
3996       struct PlayerInfo *player = &stored_player[i];
3997
3998       if (player->active && player != local_player)
3999       {
4000         int jx = player->jx, jy = player->jy;
4001
4002         player->active = FALSE;
4003         player->present = FALSE;
4004
4005         StorePlayer[jx][jy] = 0;
4006         Feld[jx][jy] = EL_EMPTY;
4007       }
4008     }
4009   }
4010
4011   for (i = 0; i < MAX_PLAYERS; i++)
4012     if (stored_player[i].active)
4013       game.players_still_needed++;
4014
4015   if (level.solved_by_one_player)
4016     game.players_still_needed = 1;
4017
4018   // when recording the game, store which players take part in the game
4019   if (tape.recording)
4020   {
4021 #if USE_NEW_PLAYER_ASSIGNMENTS
4022     for (i = 0; i < MAX_PLAYERS; i++)
4023       if (stored_player[i].connected)
4024         tape.player_participates[i] = TRUE;
4025 #else
4026     for (i = 0; i < MAX_PLAYERS; i++)
4027       if (stored_player[i].active)
4028         tape.player_participates[i] = TRUE;
4029 #endif
4030   }
4031
4032 #if DEBUG_INIT_PLAYER
4033   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4034 #endif
4035
4036   if (BorderElement == EL_EMPTY)
4037   {
4038     SBX_Left = 0;
4039     SBX_Right = lev_fieldx - SCR_FIELDX;
4040     SBY_Upper = 0;
4041     SBY_Lower = lev_fieldy - SCR_FIELDY;
4042   }
4043   else
4044   {
4045     SBX_Left = -1;
4046     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4047     SBY_Upper = -1;
4048     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4049   }
4050
4051   if (full_lev_fieldx <= SCR_FIELDX)
4052     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4053   if (full_lev_fieldy <= SCR_FIELDY)
4054     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4055
4056   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4057     SBX_Left--;
4058   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4059     SBY_Upper--;
4060
4061   // if local player not found, look for custom element that might create
4062   // the player (make some assumptions about the right custom element)
4063   if (!local_player->present)
4064   {
4065     int start_x = 0, start_y = 0;
4066     int found_rating = 0;
4067     int found_element = EL_UNDEFINED;
4068     int player_nr = local_player->index_nr;
4069
4070     SCAN_PLAYFIELD(x, y)
4071     {
4072       int element = Feld[x][y];
4073       int content;
4074       int xx, yy;
4075       boolean is_player;
4076
4077       if (level.use_start_element[player_nr] &&
4078           level.start_element[player_nr] == element &&
4079           found_rating < 4)
4080       {
4081         start_x = x;
4082         start_y = y;
4083
4084         found_rating = 4;
4085         found_element = element;
4086       }
4087
4088       if (!IS_CUSTOM_ELEMENT(element))
4089         continue;
4090
4091       if (CAN_CHANGE(element))
4092       {
4093         for (i = 0; i < element_info[element].num_change_pages; i++)
4094         {
4095           // check for player created from custom element as single target
4096           content = element_info[element].change_page[i].target_element;
4097           is_player = ELEM_IS_PLAYER(content);
4098
4099           if (is_player && (found_rating < 3 ||
4100                             (found_rating == 3 && element < found_element)))
4101           {
4102             start_x = x;
4103             start_y = y;
4104
4105             found_rating = 3;
4106             found_element = element;
4107           }
4108         }
4109       }
4110
4111       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4112       {
4113         // check for player created from custom element as explosion content
4114         content = element_info[element].content.e[xx][yy];
4115         is_player = ELEM_IS_PLAYER(content);
4116
4117         if (is_player && (found_rating < 2 ||
4118                           (found_rating == 2 && element < found_element)))
4119         {
4120           start_x = x + xx - 1;
4121           start_y = y + yy - 1;
4122
4123           found_rating = 2;
4124           found_element = element;
4125         }
4126
4127         if (!CAN_CHANGE(element))
4128           continue;
4129
4130         for (i = 0; i < element_info[element].num_change_pages; i++)
4131         {
4132           // check for player created from custom element as extended target
4133           content =
4134             element_info[element].change_page[i].target_content.e[xx][yy];
4135
4136           is_player = ELEM_IS_PLAYER(content);
4137
4138           if (is_player && (found_rating < 1 ||
4139                             (found_rating == 1 && element < found_element)))
4140           {
4141             start_x = x + xx - 1;
4142             start_y = y + yy - 1;
4143
4144             found_rating = 1;
4145             found_element = element;
4146           }
4147         }
4148       }
4149     }
4150
4151     scroll_x = SCROLL_POSITION_X(start_x);
4152     scroll_y = SCROLL_POSITION_Y(start_y);
4153   }
4154   else
4155   {
4156     scroll_x = SCROLL_POSITION_X(local_player->jx);
4157     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4158   }
4159
4160   // !!! FIX THIS (START) !!!
4161   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4162   {
4163     InitGameEngine_EM();
4164   }
4165   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4166   {
4167     InitGameEngine_SP();
4168   }
4169   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4170   {
4171     InitGameEngine_MM();
4172   }
4173   else
4174   {
4175     DrawLevel(REDRAW_FIELD);
4176     DrawAllPlayers();
4177
4178     // after drawing the level, correct some elements
4179     if (game.timegate_time_left == 0)
4180       CloseAllOpenTimegates();
4181   }
4182
4183   // blit playfield from scroll buffer to normal back buffer for fading in
4184   BlitScreenToBitmap(backbuffer);
4185   // !!! FIX THIS (END) !!!
4186
4187   DrawMaskedBorder(fade_mask);
4188
4189   FadeIn(fade_mask);
4190
4191 #if 1
4192   // full screen redraw is required at this point in the following cases:
4193   // - special editor door undrawn when game was started from level editor
4194   // - drawing area (playfield) was changed and has to be removed completely
4195   redraw_mask = REDRAW_ALL;
4196   BackToFront();
4197 #endif
4198
4199   if (!game.restart_level)
4200   {
4201     // copy default game door content to main double buffer
4202
4203     // !!! CHECK AGAIN !!!
4204     SetPanelBackground();
4205     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4206     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4207   }
4208
4209   SetPanelBackground();
4210   SetDrawBackgroundMask(REDRAW_DOOR_1);
4211
4212   UpdateAndDisplayGameControlValues();
4213
4214   if (!game.restart_level)
4215   {
4216     UnmapGameButtons();
4217     UnmapTapeButtons();
4218
4219     FreeGameButtons();
4220     CreateGameButtons();
4221
4222     MapGameButtons();
4223     MapTapeButtons();
4224
4225     // copy actual game door content to door double buffer for OpenDoor()
4226     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4227
4228     OpenDoor(DOOR_OPEN_ALL);
4229
4230     KeyboardAutoRepeatOffUnlessAutoplay();
4231
4232 #if DEBUG_INIT_PLAYER
4233     DebugPrintPlayerStatus("Player status (final)");
4234 #endif
4235   }
4236
4237   UnmapAllGadgets();
4238
4239   MapGameButtons();
4240   MapTapeButtons();
4241
4242   if (!game.restart_level && !tape.playing)
4243   {
4244     LevelStats_incPlayed(level_nr);
4245
4246     SaveLevelSetup_SeriesInfo();
4247   }
4248
4249   game.restart_level = FALSE;
4250   game.restart_game_message = NULL;
4251   game.request_active = FALSE;
4252
4253   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4254     InitGameActions_MM();
4255
4256   SaveEngineSnapshotToListInitial();
4257
4258   if (!game.restart_level)
4259   {
4260     PlaySound(SND_GAME_STARTING);
4261
4262     if (setup.sound_music)
4263       PlayLevelMusic();
4264   }
4265 }
4266
4267 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4268                         int actual_player_x, int actual_player_y)
4269 {
4270   // this is used for non-R'n'D game engines to update certain engine values
4271
4272   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4273   {
4274     actual_player_x = correctLevelPosX_EM(actual_player_x);
4275     actual_player_y = correctLevelPosY_EM(actual_player_y);
4276   }
4277
4278   // needed to determine if sounds are played within the visible screen area
4279   scroll_x = actual_scroll_x;
4280   scroll_y = actual_scroll_y;
4281
4282   // needed to get player position for "follow finger" playing input method
4283   local_player->jx = actual_player_x;
4284   local_player->jy = actual_player_y;
4285 }
4286
4287 void InitMovDir(int x, int y)
4288 {
4289   int i, element = Feld[x][y];
4290   static int xy[4][2] =
4291   {
4292     {  0, +1 },
4293     { +1,  0 },
4294     {  0, -1 },
4295     { -1,  0 }
4296   };
4297   static int direction[3][4] =
4298   {
4299     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4300     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4301     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4302   };
4303
4304   switch (element)
4305   {
4306     case EL_BUG_RIGHT:
4307     case EL_BUG_UP:
4308     case EL_BUG_LEFT:
4309     case EL_BUG_DOWN:
4310       Feld[x][y] = EL_BUG;
4311       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4312       break;
4313
4314     case EL_SPACESHIP_RIGHT:
4315     case EL_SPACESHIP_UP:
4316     case EL_SPACESHIP_LEFT:
4317     case EL_SPACESHIP_DOWN:
4318       Feld[x][y] = EL_SPACESHIP;
4319       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4320       break;
4321
4322     case EL_BD_BUTTERFLY_RIGHT:
4323     case EL_BD_BUTTERFLY_UP:
4324     case EL_BD_BUTTERFLY_LEFT:
4325     case EL_BD_BUTTERFLY_DOWN:
4326       Feld[x][y] = EL_BD_BUTTERFLY;
4327       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4328       break;
4329
4330     case EL_BD_FIREFLY_RIGHT:
4331     case EL_BD_FIREFLY_UP:
4332     case EL_BD_FIREFLY_LEFT:
4333     case EL_BD_FIREFLY_DOWN:
4334       Feld[x][y] = EL_BD_FIREFLY;
4335       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4336       break;
4337
4338     case EL_PACMAN_RIGHT:
4339     case EL_PACMAN_UP:
4340     case EL_PACMAN_LEFT:
4341     case EL_PACMAN_DOWN:
4342       Feld[x][y] = EL_PACMAN;
4343       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4344       break;
4345
4346     case EL_YAMYAM_LEFT:
4347     case EL_YAMYAM_RIGHT:
4348     case EL_YAMYAM_UP:
4349     case EL_YAMYAM_DOWN:
4350       Feld[x][y] = EL_YAMYAM;
4351       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4352       break;
4353
4354     case EL_SP_SNIKSNAK:
4355       MovDir[x][y] = MV_UP;
4356       break;
4357
4358     case EL_SP_ELECTRON:
4359       MovDir[x][y] = MV_LEFT;
4360       break;
4361
4362     case EL_MOLE_LEFT:
4363     case EL_MOLE_RIGHT:
4364     case EL_MOLE_UP:
4365     case EL_MOLE_DOWN:
4366       Feld[x][y] = EL_MOLE;
4367       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4368       break;
4369
4370     default:
4371       if (IS_CUSTOM_ELEMENT(element))
4372       {
4373         struct ElementInfo *ei = &element_info[element];
4374         int move_direction_initial = ei->move_direction_initial;
4375         int move_pattern = ei->move_pattern;
4376
4377         if (move_direction_initial == MV_START_PREVIOUS)
4378         {
4379           if (MovDir[x][y] != MV_NONE)
4380             return;
4381
4382           move_direction_initial = MV_START_AUTOMATIC;
4383         }
4384
4385         if (move_direction_initial == MV_START_RANDOM)
4386           MovDir[x][y] = 1 << RND(4);
4387         else if (move_direction_initial & MV_ANY_DIRECTION)
4388           MovDir[x][y] = move_direction_initial;
4389         else if (move_pattern == MV_ALL_DIRECTIONS ||
4390                  move_pattern == MV_TURNING_LEFT ||
4391                  move_pattern == MV_TURNING_RIGHT ||
4392                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4393                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4394                  move_pattern == MV_TURNING_RANDOM)
4395           MovDir[x][y] = 1 << RND(4);
4396         else if (move_pattern == MV_HORIZONTAL)
4397           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4398         else if (move_pattern == MV_VERTICAL)
4399           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4400         else if (move_pattern & MV_ANY_DIRECTION)
4401           MovDir[x][y] = element_info[element].move_pattern;
4402         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4403                  move_pattern == MV_ALONG_RIGHT_SIDE)
4404         {
4405           // use random direction as default start direction
4406           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4407             MovDir[x][y] = 1 << RND(4);
4408
4409           for (i = 0; i < NUM_DIRECTIONS; i++)
4410           {
4411             int x1 = x + xy[i][0];
4412             int y1 = y + xy[i][1];
4413
4414             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4415             {
4416               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4417                 MovDir[x][y] = direction[0][i];
4418               else
4419                 MovDir[x][y] = direction[1][i];
4420
4421               break;
4422             }
4423           }
4424         }                
4425       }
4426       else
4427       {
4428         MovDir[x][y] = 1 << RND(4);
4429
4430         if (element != EL_BUG &&
4431             element != EL_SPACESHIP &&
4432             element != EL_BD_BUTTERFLY &&
4433             element != EL_BD_FIREFLY)
4434           break;
4435
4436         for (i = 0; i < NUM_DIRECTIONS; i++)
4437         {
4438           int x1 = x + xy[i][0];
4439           int y1 = y + xy[i][1];
4440
4441           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4442           {
4443             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4444             {
4445               MovDir[x][y] = direction[0][i];
4446               break;
4447             }
4448             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4449                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4450             {
4451               MovDir[x][y] = direction[1][i];
4452               break;
4453             }
4454           }
4455         }
4456       }
4457       break;
4458   }
4459
4460   GfxDir[x][y] = MovDir[x][y];
4461 }
4462
4463 void InitAmoebaNr(int x, int y)
4464 {
4465   int i;
4466   int group_nr = AmoebeNachbarNr(x, y);
4467
4468   if (group_nr == 0)
4469   {
4470     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4471     {
4472       if (AmoebaCnt[i] == 0)
4473       {
4474         group_nr = i;
4475         break;
4476       }
4477     }
4478   }
4479
4480   AmoebaNr[x][y] = group_nr;
4481   AmoebaCnt[group_nr]++;
4482   AmoebaCnt2[group_nr]++;
4483 }
4484
4485 static void LevelSolved(void)
4486 {
4487   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4488       game.players_still_needed > 0)
4489     return;
4490
4491   game.LevelSolved = TRUE;
4492   game.GameOver = TRUE;
4493
4494   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4495                       level.native_em_level->lev->score :
4496                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4497                       game_mm.score :
4498                       game.score);
4499   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4500                        MM_HEALTH(game_mm.laser_overload_value) :
4501                        game.health);
4502
4503   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4504   game.LevelSolved_CountingScore = game.score_final;
4505   game.LevelSolved_CountingHealth = game.health_final;
4506 }
4507
4508 void GameWon(void)
4509 {
4510   static int time_count_steps;
4511   static int time, time_final;
4512   static int score, score_final;
4513   static int health, health_final;
4514   static int game_over_delay_1 = 0;
4515   static int game_over_delay_2 = 0;
4516   static int game_over_delay_3 = 0;
4517   int game_over_delay_value_1 = 50;
4518   int game_over_delay_value_2 = 25;
4519   int game_over_delay_value_3 = 50;
4520
4521   if (!game.LevelSolved_GameWon)
4522   {
4523     int i;
4524
4525     // do not start end game actions before the player stops moving (to exit)
4526     if (local_player->active && local_player->MovPos)
4527       return;
4528
4529     game.LevelSolved_GameWon = TRUE;
4530     game.LevelSolved_SaveTape = tape.recording;
4531     game.LevelSolved_SaveScore = !tape.playing;
4532
4533     if (!tape.playing)
4534     {
4535       LevelStats_incSolved(level_nr);
4536
4537       SaveLevelSetup_SeriesInfo();
4538     }
4539
4540     if (tape.auto_play)         // tape might already be stopped here
4541       tape.auto_play_level_solved = TRUE;
4542
4543     TapeStop();
4544
4545     game_over_delay_1 = 0;
4546     game_over_delay_2 = 0;
4547     game_over_delay_3 = game_over_delay_value_3;
4548
4549     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4550     score = score_final = game.score_final;
4551     health = health_final = game.health_final;
4552
4553     if (level.score[SC_TIME_BONUS] > 0)
4554     {
4555       if (TimeLeft > 0)
4556       {
4557         time_final = 0;
4558         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4559       }
4560       else if (game.no_time_limit && TimePlayed < 999)
4561       {
4562         time_final = 999;
4563         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4564       }
4565
4566       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4567
4568       game_over_delay_1 = game_over_delay_value_1;
4569
4570       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4571       {
4572         health_final = 0;
4573         score_final += health * level.score[SC_TIME_BONUS];
4574
4575         game_over_delay_2 = game_over_delay_value_2;
4576       }
4577
4578       game.score_final = score_final;
4579       game.health_final = health_final;
4580     }
4581
4582     if (level_editor_test_game)
4583     {
4584       time = time_final;
4585       score = score_final;
4586
4587       game.LevelSolved_CountingTime = time;
4588       game.LevelSolved_CountingScore = score;
4589
4590       game_panel_controls[GAME_PANEL_TIME].value = time;
4591       game_panel_controls[GAME_PANEL_SCORE].value = score;
4592
4593       DisplayGameControlValues();
4594     }
4595
4596     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4597     {
4598       // check if last player has left the level
4599       if (game.exit_x >= 0 &&
4600           game.exit_y >= 0)
4601       {
4602         int x = game.exit_x;
4603         int y = game.exit_y;
4604         int element = Feld[x][y];
4605
4606         // close exit door after last player
4607         if ((game.all_players_gone &&
4608              (element == EL_EXIT_OPEN ||
4609               element == EL_SP_EXIT_OPEN ||
4610               element == EL_STEEL_EXIT_OPEN)) ||
4611             element == EL_EM_EXIT_OPEN ||
4612             element == EL_EM_STEEL_EXIT_OPEN)
4613         {
4614
4615           Feld[x][y] =
4616             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4617              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4618              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4619              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4620              EL_EM_STEEL_EXIT_CLOSING);
4621
4622           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4623         }
4624
4625         // player disappears
4626         DrawLevelField(x, y);
4627       }
4628
4629       for (i = 0; i < MAX_PLAYERS; i++)
4630       {
4631         struct PlayerInfo *player = &stored_player[i];
4632
4633         if (player->present)
4634         {
4635           RemovePlayer(player);
4636
4637           // player disappears
4638           DrawLevelField(player->jx, player->jy);
4639         }
4640       }
4641     }
4642
4643     PlaySound(SND_GAME_WINNING);
4644   }
4645
4646   if (game_over_delay_1 > 0)
4647   {
4648     game_over_delay_1--;
4649
4650     return;
4651   }
4652
4653   if (time != time_final)
4654   {
4655     int time_to_go = ABS(time_final - time);
4656     int time_count_dir = (time < time_final ? +1 : -1);
4657
4658     if (time_to_go < time_count_steps)
4659       time_count_steps = 1;
4660
4661     time  += time_count_steps * time_count_dir;
4662     score += time_count_steps * level.score[SC_TIME_BONUS];
4663
4664     game.LevelSolved_CountingTime = time;
4665     game.LevelSolved_CountingScore = score;
4666
4667     game_panel_controls[GAME_PANEL_TIME].value = time;
4668     game_panel_controls[GAME_PANEL_SCORE].value = score;
4669
4670     DisplayGameControlValues();
4671
4672     if (time == time_final)
4673       StopSound(SND_GAME_LEVELTIME_BONUS);
4674     else if (setup.sound_loops)
4675       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4676     else
4677       PlaySound(SND_GAME_LEVELTIME_BONUS);
4678
4679     return;
4680   }
4681
4682   if (game_over_delay_2 > 0)
4683   {
4684     game_over_delay_2--;
4685
4686     return;
4687   }
4688
4689   if (health != health_final)
4690   {
4691     int health_count_dir = (health < health_final ? +1 : -1);
4692
4693     health += health_count_dir;
4694     score  += level.score[SC_TIME_BONUS];
4695
4696     game.LevelSolved_CountingHealth = health;
4697     game.LevelSolved_CountingScore = score;
4698
4699     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4700     game_panel_controls[GAME_PANEL_SCORE].value = score;
4701
4702     DisplayGameControlValues();
4703
4704     if (health == health_final)
4705       StopSound(SND_GAME_LEVELTIME_BONUS);
4706     else if (setup.sound_loops)
4707       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4708     else
4709       PlaySound(SND_GAME_LEVELTIME_BONUS);
4710
4711     return;
4712   }
4713
4714   game.panel.active = FALSE;
4715
4716   if (game_over_delay_3 > 0)
4717   {
4718     game_over_delay_3--;
4719
4720     return;
4721   }
4722
4723   GameEnd();
4724 }
4725
4726 void GameEnd(void)
4727 {
4728   // used instead of "level_nr" (needed for network games)
4729   int last_level_nr = levelset.level_nr;
4730   int hi_pos;
4731
4732   game.LevelSolved_GameEnd = TRUE;
4733
4734   if (game.LevelSolved_SaveTape)
4735   {
4736     // make sure that request dialog to save tape does not open door again
4737     if (!global.use_envelope_request)
4738       CloseDoor(DOOR_CLOSE_1);
4739
4740     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4741   }
4742
4743   // if no tape is to be saved, close both doors simultaneously
4744   CloseDoor(DOOR_CLOSE_ALL);
4745
4746   if (level_editor_test_game)
4747   {
4748     SetGameStatus(GAME_MODE_MAIN);
4749
4750     DrawMainMenu();
4751
4752     return;
4753   }
4754
4755   if (!game.LevelSolved_SaveScore)
4756   {
4757     SetGameStatus(GAME_MODE_MAIN);
4758
4759     DrawMainMenu();
4760
4761     return;
4762   }
4763
4764   if (level_nr == leveldir_current->handicap_level)
4765   {
4766     leveldir_current->handicap_level++;
4767
4768     SaveLevelSetup_SeriesInfo();
4769   }
4770
4771   if (setup.increment_levels &&
4772       level_nr < leveldir_current->last_level &&
4773       !network_playing)
4774   {
4775     level_nr++;         // advance to next level
4776     TapeErase();        // start with empty tape
4777
4778     if (setup.auto_play_next_level)
4779     {
4780       LoadLevel(level_nr);
4781
4782       SaveLevelSetup_SeriesInfo();
4783     }
4784   }
4785
4786   hi_pos = NewHiScore(last_level_nr);
4787
4788   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4789   {
4790     SetGameStatus(GAME_MODE_SCORES);
4791
4792     DrawHallOfFame(last_level_nr, hi_pos);
4793   }
4794   else if (setup.auto_play_next_level && setup.increment_levels &&
4795            last_level_nr < leveldir_current->last_level &&
4796            !network_playing)
4797   {
4798     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4799   }
4800   else
4801   {
4802     SetGameStatus(GAME_MODE_MAIN);
4803
4804     DrawMainMenu();
4805   }
4806 }
4807
4808 int NewHiScore(int level_nr)
4809 {
4810   int k, l;
4811   int position = -1;
4812   boolean one_score_entry_per_name = !program.many_scores_per_name;
4813
4814   LoadScore(level_nr);
4815
4816   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4817       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4818     return -1;
4819
4820   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4821   {
4822     if (game.score_final > highscore[k].Score)
4823     {
4824       // player has made it to the hall of fame
4825
4826       if (k < MAX_SCORE_ENTRIES - 1)
4827       {
4828         int m = MAX_SCORE_ENTRIES - 1;
4829
4830         if (one_score_entry_per_name)
4831         {
4832           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4833             if (strEqual(setup.player_name, highscore[l].Name))
4834               m = l;
4835
4836           if (m == k)   // player's new highscore overwrites his old one
4837             goto put_into_list;
4838         }
4839
4840         for (l = m; l > k; l--)
4841         {
4842           strcpy(highscore[l].Name, highscore[l - 1].Name);
4843           highscore[l].Score = highscore[l - 1].Score;
4844         }
4845       }
4846
4847       put_into_list:
4848
4849       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4850       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4851       highscore[k].Score = game.score_final;
4852       position = k;
4853
4854       break;
4855     }
4856     else if (one_score_entry_per_name &&
4857              !strncmp(setup.player_name, highscore[k].Name,
4858                       MAX_PLAYER_NAME_LEN))
4859       break;    // player already there with a higher score
4860   }
4861
4862   if (position >= 0) 
4863     SaveScore(level_nr);
4864
4865   return position;
4866 }
4867
4868 static int getElementMoveStepsizeExt(int x, int y, int direction)
4869 {
4870   int element = Feld[x][y];
4871   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4872   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4873   int horiz_move = (dx != 0);
4874   int sign = (horiz_move ? dx : dy);
4875   int step = sign * element_info[element].move_stepsize;
4876
4877   // special values for move stepsize for spring and things on conveyor belt
4878   if (horiz_move)
4879   {
4880     if (CAN_FALL(element) &&
4881         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4882       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4883     else if (element == EL_SPRING)
4884       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4885   }
4886
4887   return step;
4888 }
4889
4890 static int getElementMoveStepsize(int x, int y)
4891 {
4892   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4893 }
4894
4895 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4896 {
4897   if (player->GfxAction != action || player->GfxDir != dir)
4898   {
4899     player->GfxAction = action;
4900     player->GfxDir = dir;
4901     player->Frame = 0;
4902     player->StepFrame = 0;
4903   }
4904 }
4905
4906 static void ResetGfxFrame(int x, int y)
4907 {
4908   // profiling showed that "autotest" spends 10~20% of its time in this function
4909   if (DrawingDeactivatedField())
4910     return;
4911
4912   int element = Feld[x][y];
4913   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4914
4915   if (graphic_info[graphic].anim_global_sync)
4916     GfxFrame[x][y] = FrameCounter;
4917   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4918     GfxFrame[x][y] = CustomValue[x][y];
4919   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4920     GfxFrame[x][y] = element_info[element].collect_score;
4921   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4922     GfxFrame[x][y] = ChangeDelay[x][y];
4923 }
4924
4925 static void ResetGfxAnimation(int x, int y)
4926 {
4927   GfxAction[x][y] = ACTION_DEFAULT;
4928   GfxDir[x][y] = MovDir[x][y];
4929   GfxFrame[x][y] = 0;
4930
4931   ResetGfxFrame(x, y);
4932 }
4933
4934 static void ResetRandomAnimationValue(int x, int y)
4935 {
4936   GfxRandom[x][y] = INIT_GFX_RANDOM();
4937 }
4938
4939 static void InitMovingField(int x, int y, int direction)
4940 {
4941   int element = Feld[x][y];
4942   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4943   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4944   int newx = x + dx;
4945   int newy = y + dy;
4946   boolean is_moving_before, is_moving_after;
4947
4948   // check if element was/is moving or being moved before/after mode change
4949   is_moving_before = (WasJustMoving[x][y] != 0);
4950   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4951
4952   // reset animation only for moving elements which change direction of moving
4953   // or which just started or stopped moving
4954   // (else CEs with property "can move" / "not moving" are reset each frame)
4955   if (is_moving_before != is_moving_after ||
4956       direction != MovDir[x][y])
4957     ResetGfxAnimation(x, y);
4958
4959   MovDir[x][y] = direction;
4960   GfxDir[x][y] = direction;
4961
4962   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4963                      direction == MV_DOWN && CAN_FALL(element) ?
4964                      ACTION_FALLING : ACTION_MOVING);
4965
4966   // this is needed for CEs with property "can move" / "not moving"
4967
4968   if (is_moving_after)
4969   {
4970     if (Feld[newx][newy] == EL_EMPTY)
4971       Feld[newx][newy] = EL_BLOCKED;
4972
4973     MovDir[newx][newy] = MovDir[x][y];
4974
4975     CustomValue[newx][newy] = CustomValue[x][y];
4976
4977     GfxFrame[newx][newy] = GfxFrame[x][y];
4978     GfxRandom[newx][newy] = GfxRandom[x][y];
4979     GfxAction[newx][newy] = GfxAction[x][y];
4980     GfxDir[newx][newy] = GfxDir[x][y];
4981   }
4982 }
4983
4984 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4985 {
4986   int direction = MovDir[x][y];
4987   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4988   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4989
4990   *goes_to_x = newx;
4991   *goes_to_y = newy;
4992 }
4993
4994 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4995 {
4996   int oldx = x, oldy = y;
4997   int direction = MovDir[x][y];
4998
4999   if (direction == MV_LEFT)
5000     oldx++;
5001   else if (direction == MV_RIGHT)
5002     oldx--;
5003   else if (direction == MV_UP)
5004     oldy++;
5005   else if (direction == MV_DOWN)
5006     oldy--;
5007
5008   *comes_from_x = oldx;
5009   *comes_from_y = oldy;
5010 }
5011
5012 static int MovingOrBlocked2Element(int x, int y)
5013 {
5014   int element = Feld[x][y];
5015
5016   if (element == EL_BLOCKED)
5017   {
5018     int oldx, oldy;
5019
5020     Blocked2Moving(x, y, &oldx, &oldy);
5021     return Feld[oldx][oldy];
5022   }
5023   else
5024     return element;
5025 }
5026
5027 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5028 {
5029   // like MovingOrBlocked2Element(), but if element is moving
5030   // and (x,y) is the field the moving element is just leaving,
5031   // return EL_BLOCKED instead of the element value
5032   int element = Feld[x][y];
5033
5034   if (IS_MOVING(x, y))
5035   {
5036     if (element == EL_BLOCKED)
5037     {
5038       int oldx, oldy;
5039
5040       Blocked2Moving(x, y, &oldx, &oldy);
5041       return Feld[oldx][oldy];
5042     }
5043     else
5044       return EL_BLOCKED;
5045   }
5046   else
5047     return element;
5048 }
5049
5050 static void RemoveField(int x, int y)
5051 {
5052   Feld[x][y] = EL_EMPTY;
5053
5054   MovPos[x][y] = 0;
5055   MovDir[x][y] = 0;
5056   MovDelay[x][y] = 0;
5057
5058   CustomValue[x][y] = 0;
5059
5060   AmoebaNr[x][y] = 0;
5061   ChangeDelay[x][y] = 0;
5062   ChangePage[x][y] = -1;
5063   Pushed[x][y] = FALSE;
5064
5065   GfxElement[x][y] = EL_UNDEFINED;
5066   GfxAction[x][y] = ACTION_DEFAULT;
5067   GfxDir[x][y] = MV_NONE;
5068 }
5069
5070 static void RemoveMovingField(int x, int y)
5071 {
5072   int oldx = x, oldy = y, newx = x, newy = y;
5073   int element = Feld[x][y];
5074   int next_element = EL_UNDEFINED;
5075
5076   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5077     return;
5078
5079   if (IS_MOVING(x, y))
5080   {
5081     Moving2Blocked(x, y, &newx, &newy);
5082
5083     if (Feld[newx][newy] != EL_BLOCKED)
5084     {
5085       // element is moving, but target field is not free (blocked), but
5086       // already occupied by something different (example: acid pool);
5087       // in this case, only remove the moving field, but not the target
5088
5089       RemoveField(oldx, oldy);
5090
5091       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5092
5093       TEST_DrawLevelField(oldx, oldy);
5094
5095       return;
5096     }
5097   }
5098   else if (element == EL_BLOCKED)
5099   {
5100     Blocked2Moving(x, y, &oldx, &oldy);
5101     if (!IS_MOVING(oldx, oldy))
5102       return;
5103   }
5104
5105   if (element == EL_BLOCKED &&
5106       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5107        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5108        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5109        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5110        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5111        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5112     next_element = get_next_element(Feld[oldx][oldy]);
5113
5114   RemoveField(oldx, oldy);
5115   RemoveField(newx, newy);
5116
5117   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5118
5119   if (next_element != EL_UNDEFINED)
5120     Feld[oldx][oldy] = next_element;
5121
5122   TEST_DrawLevelField(oldx, oldy);
5123   TEST_DrawLevelField(newx, newy);
5124 }
5125
5126 void DrawDynamite(int x, int y)
5127 {
5128   int sx = SCREENX(x), sy = SCREENY(y);
5129   int graphic = el2img(Feld[x][y]);
5130   int frame;
5131
5132   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5133     return;
5134
5135   if (IS_WALKABLE_INSIDE(Back[x][y]))
5136     return;
5137
5138   if (Back[x][y])
5139     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5140   else if (Store[x][y])
5141     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5142
5143   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5144
5145   if (Back[x][y] || Store[x][y])
5146     DrawGraphicThruMask(sx, sy, graphic, frame);
5147   else
5148     DrawGraphic(sx, sy, graphic, frame);
5149 }
5150
5151 static void CheckDynamite(int x, int y)
5152 {
5153   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5154   {
5155     MovDelay[x][y]--;
5156
5157     if (MovDelay[x][y] != 0)
5158     {
5159       DrawDynamite(x, y);
5160       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5161
5162       return;
5163     }
5164   }
5165
5166   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5167
5168   Bang(x, y);
5169 }
5170
5171 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5172 {
5173   boolean num_checked_players = 0;
5174   int i;
5175
5176   for (i = 0; i < MAX_PLAYERS; i++)
5177   {
5178     if (stored_player[i].active)
5179     {
5180       int sx = stored_player[i].jx;
5181       int sy = stored_player[i].jy;
5182
5183       if (num_checked_players == 0)
5184       {
5185         *sx1 = *sx2 = sx;
5186         *sy1 = *sy2 = sy;
5187       }
5188       else
5189       {
5190         *sx1 = MIN(*sx1, sx);
5191         *sy1 = MIN(*sy1, sy);
5192         *sx2 = MAX(*sx2, sx);
5193         *sy2 = MAX(*sy2, sy);
5194       }
5195
5196       num_checked_players++;
5197     }
5198   }
5199 }
5200
5201 static boolean checkIfAllPlayersFitToScreen_RND(void)
5202 {
5203   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5204
5205   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5206
5207   return (sx2 - sx1 < SCR_FIELDX &&
5208           sy2 - sy1 < SCR_FIELDY);
5209 }
5210
5211 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5212 {
5213   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5214
5215   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5216
5217   *sx = (sx1 + sx2) / 2;
5218   *sy = (sy1 + sy2) / 2;
5219 }
5220
5221 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5222                                boolean center_screen, boolean quick_relocation)
5223 {
5224   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5225   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5226   boolean no_delay = (tape.warp_forward);
5227   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5228   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5229   int new_scroll_x, new_scroll_y;
5230
5231   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5232   {
5233     // case 1: quick relocation inside visible screen (without scrolling)
5234
5235     RedrawPlayfield();
5236
5237     return;
5238   }
5239
5240   if (!level.shifted_relocation || center_screen)
5241   {
5242     // relocation _with_ centering of screen
5243
5244     new_scroll_x = SCROLL_POSITION_X(x);
5245     new_scroll_y = SCROLL_POSITION_Y(y);
5246   }
5247   else
5248   {
5249     // relocation _without_ centering of screen
5250
5251     int center_scroll_x = SCROLL_POSITION_X(old_x);
5252     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5253     int offset_x = x + (scroll_x - center_scroll_x);
5254     int offset_y = y + (scroll_y - center_scroll_y);
5255
5256     // for new screen position, apply previous offset to center position
5257     new_scroll_x = SCROLL_POSITION_X(offset_x);
5258     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5259   }
5260
5261   if (quick_relocation)
5262   {
5263     // case 2: quick relocation (redraw without visible scrolling)
5264
5265     scroll_x = new_scroll_x;
5266     scroll_y = new_scroll_y;
5267
5268     RedrawPlayfield();
5269
5270     return;
5271   }
5272
5273   // case 3: visible relocation (with scrolling to new position)
5274
5275   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5276
5277   SetVideoFrameDelay(wait_delay_value);
5278
5279   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5280   {
5281     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5282     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5283
5284     if (dx == 0 && dy == 0)             // no scrolling needed at all
5285       break;
5286
5287     scroll_x -= dx;
5288     scroll_y -= dy;
5289
5290     // set values for horizontal/vertical screen scrolling (half tile size)
5291     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5292     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5293     int pos_x = dx * TILEX / 2;
5294     int pos_y = dy * TILEY / 2;
5295     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5296     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5297
5298     ScrollLevel(dx, dy);
5299     DrawAllPlayers();
5300
5301     // scroll in two steps of half tile size to make things smoother
5302     BlitScreenToBitmapExt_RND(window, fx, fy);
5303
5304     // scroll second step to align at full tile size
5305     BlitScreenToBitmap(window);
5306   }
5307
5308   DrawAllPlayers();
5309   BackToFront();
5310
5311   SetVideoFrameDelay(frame_delay_value_old);
5312 }
5313
5314 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5315 {
5316   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5317   int player_nr = GET_PLAYER_NR(el_player);
5318   struct PlayerInfo *player = &stored_player[player_nr];
5319   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5320   boolean no_delay = (tape.warp_forward);
5321   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5322   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5323   int old_jx = player->jx;
5324   int old_jy = player->jy;
5325   int old_element = Feld[old_jx][old_jy];
5326   int element = Feld[jx][jy];
5327   boolean player_relocated = (old_jx != jx || old_jy != jy);
5328
5329   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5330   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5331   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5332   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5333   int leave_side_horiz = move_dir_horiz;
5334   int leave_side_vert  = move_dir_vert;
5335   int enter_side = enter_side_horiz | enter_side_vert;
5336   int leave_side = leave_side_horiz | leave_side_vert;
5337
5338   if (player->buried)           // do not reanimate dead player
5339     return;
5340
5341   if (!player_relocated)        // no need to relocate the player
5342     return;
5343
5344   if (IS_PLAYER(jx, jy))        // player already placed at new position
5345   {
5346     RemoveField(jx, jy);        // temporarily remove newly placed player
5347     DrawLevelField(jx, jy);
5348   }
5349
5350   if (player->present)
5351   {
5352     while (player->MovPos)
5353     {
5354       ScrollPlayer(player, SCROLL_GO_ON);
5355       ScrollScreen(NULL, SCROLL_GO_ON);
5356
5357       AdvanceFrameAndPlayerCounters(player->index_nr);
5358
5359       DrawPlayer(player);
5360
5361       BackToFront_WithFrameDelay(wait_delay_value);
5362     }
5363
5364     DrawPlayer(player);         // needed here only to cleanup last field
5365     DrawLevelField(player->jx, player->jy);     // remove player graphic
5366
5367     player->is_moving = FALSE;
5368   }
5369
5370   if (IS_CUSTOM_ELEMENT(old_element))
5371     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5372                                CE_LEFT_BY_PLAYER,
5373                                player->index_bit, leave_side);
5374
5375   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5376                                       CE_PLAYER_LEAVES_X,
5377                                       player->index_bit, leave_side);
5378
5379   Feld[jx][jy] = el_player;
5380   InitPlayerField(jx, jy, el_player, TRUE);
5381
5382   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5383      possible that the relocation target field did not contain a player element,
5384      but a walkable element, to which the new player was relocated -- in this
5385      case, restore that (already initialized!) element on the player field */
5386   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5387   {
5388     Feld[jx][jy] = element;     // restore previously existing element
5389   }
5390
5391   // only visually relocate centered player
5392   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5393                      FALSE, level.instant_relocation);
5394
5395   TestIfPlayerTouchesBadThing(jx, jy);
5396   TestIfPlayerTouchesCustomElement(jx, jy);
5397
5398   if (IS_CUSTOM_ELEMENT(element))
5399     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5400                                player->index_bit, enter_side);
5401
5402   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5403                                       player->index_bit, enter_side);
5404
5405   if (player->is_switching)
5406   {
5407     /* ensure that relocation while still switching an element does not cause
5408        a new element to be treated as also switched directly after relocation
5409        (this is important for teleporter switches that teleport the player to
5410        a place where another teleporter switch is in the same direction, which
5411        would then incorrectly be treated as immediately switched before the
5412        direction key that caused the switch was released) */
5413
5414     player->switch_x += jx - old_jx;
5415     player->switch_y += jy - old_jy;
5416   }
5417 }
5418
5419 static void Explode(int ex, int ey, int phase, int mode)
5420 {
5421   int x, y;
5422   int last_phase;
5423   int border_element;
5424
5425   // !!! eliminate this variable !!!
5426   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5427
5428   if (game.explosions_delayed)
5429   {
5430     ExplodeField[ex][ey] = mode;
5431     return;
5432   }
5433
5434   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5435   {
5436     int center_element = Feld[ex][ey];
5437     int artwork_element, explosion_element;     // set these values later
5438
5439     // remove things displayed in background while burning dynamite
5440     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5441       Back[ex][ey] = 0;
5442
5443     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5444     {
5445       // put moving element to center field (and let it explode there)
5446       center_element = MovingOrBlocked2Element(ex, ey);
5447       RemoveMovingField(ex, ey);
5448       Feld[ex][ey] = center_element;
5449     }
5450
5451     // now "center_element" is finally determined -- set related values now
5452     artwork_element = center_element;           // for custom player artwork
5453     explosion_element = center_element;         // for custom player artwork
5454
5455     if (IS_PLAYER(ex, ey))
5456     {
5457       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5458
5459       artwork_element = stored_player[player_nr].artwork_element;
5460
5461       if (level.use_explosion_element[player_nr])
5462       {
5463         explosion_element = level.explosion_element[player_nr];
5464         artwork_element = explosion_element;
5465       }
5466     }
5467
5468     if (mode == EX_TYPE_NORMAL ||
5469         mode == EX_TYPE_CENTER ||
5470         mode == EX_TYPE_CROSS)
5471       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5472
5473     last_phase = element_info[explosion_element].explosion_delay + 1;
5474
5475     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5476     {
5477       int xx = x - ex + 1;
5478       int yy = y - ey + 1;
5479       int element;
5480
5481       if (!IN_LEV_FIELD(x, y) ||
5482           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5483           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5484         continue;
5485
5486       element = Feld[x][y];
5487
5488       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5489       {
5490         element = MovingOrBlocked2Element(x, y);
5491
5492         if (!IS_EXPLOSION_PROOF(element))
5493           RemoveMovingField(x, y);
5494       }
5495
5496       // indestructible elements can only explode in center (but not flames)
5497       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5498                                            mode == EX_TYPE_BORDER)) ||
5499           element == EL_FLAMES)
5500         continue;
5501
5502       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5503          behaviour, for example when touching a yamyam that explodes to rocks
5504          with active deadly shield, a rock is created under the player !!! */
5505       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5506 #if 0
5507       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5508           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5509            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5510 #else
5511       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5512 #endif
5513       {
5514         if (IS_ACTIVE_BOMB(element))
5515         {
5516           // re-activate things under the bomb like gate or penguin
5517           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5518           Back[x][y] = 0;
5519         }
5520
5521         continue;
5522       }
5523
5524       // save walkable background elements while explosion on same tile
5525       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5526           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5527         Back[x][y] = element;
5528
5529       // ignite explodable elements reached by other explosion
5530       if (element == EL_EXPLOSION)
5531         element = Store2[x][y];
5532
5533       if (AmoebaNr[x][y] &&
5534           (element == EL_AMOEBA_FULL ||
5535            element == EL_BD_AMOEBA ||
5536            element == EL_AMOEBA_GROWING))
5537       {
5538         AmoebaCnt[AmoebaNr[x][y]]--;
5539         AmoebaCnt2[AmoebaNr[x][y]]--;
5540       }
5541
5542       RemoveField(x, y);
5543
5544       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5545       {
5546         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5547
5548         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5549
5550         if (PLAYERINFO(ex, ey)->use_murphy)
5551           Store[x][y] = EL_EMPTY;
5552       }
5553
5554       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5555       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5556       else if (ELEM_IS_PLAYER(center_element))
5557         Store[x][y] = EL_EMPTY;
5558       else if (center_element == EL_YAMYAM)
5559         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5560       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5561         Store[x][y] = element_info[center_element].content.e[xx][yy];
5562 #if 1
5563       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5564       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5565       // otherwise) -- FIX THIS !!!
5566       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5567         Store[x][y] = element_info[element].content.e[1][1];
5568 #else
5569       else if (!CAN_EXPLODE(element))
5570         Store[x][y] = element_info[element].content.e[1][1];
5571 #endif
5572       else
5573         Store[x][y] = EL_EMPTY;
5574
5575       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5576           center_element == EL_AMOEBA_TO_DIAMOND)
5577         Store2[x][y] = element;
5578
5579       Feld[x][y] = EL_EXPLOSION;
5580       GfxElement[x][y] = artwork_element;
5581
5582       ExplodePhase[x][y] = 1;
5583       ExplodeDelay[x][y] = last_phase;
5584
5585       Stop[x][y] = TRUE;
5586     }
5587
5588     if (center_element == EL_YAMYAM)
5589       game.yamyam_content_nr =
5590         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5591
5592     return;
5593   }
5594
5595   if (Stop[ex][ey])
5596     return;
5597
5598   x = ex;
5599   y = ey;
5600
5601   if (phase == 1)
5602     GfxFrame[x][y] = 0;         // restart explosion animation
5603
5604   last_phase = ExplodeDelay[x][y];
5605
5606   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5607
5608   // this can happen if the player leaves an explosion just in time
5609   if (GfxElement[x][y] == EL_UNDEFINED)
5610     GfxElement[x][y] = EL_EMPTY;
5611
5612   border_element = Store2[x][y];
5613   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5614     border_element = StorePlayer[x][y];
5615
5616   if (phase == element_info[border_element].ignition_delay ||
5617       phase == last_phase)
5618   {
5619     boolean border_explosion = FALSE;
5620
5621     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5622         !PLAYER_EXPLOSION_PROTECTED(x, y))
5623     {
5624       KillPlayerUnlessExplosionProtected(x, y);
5625       border_explosion = TRUE;
5626     }
5627     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5628     {
5629       Feld[x][y] = Store2[x][y];
5630       Store2[x][y] = 0;
5631       Bang(x, y);
5632       border_explosion = TRUE;
5633     }
5634     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5635     {
5636       AmoebeUmwandeln(x, y);
5637       Store2[x][y] = 0;
5638       border_explosion = TRUE;
5639     }
5640
5641     // if an element just explodes due to another explosion (chain-reaction),
5642     // do not immediately end the new explosion when it was the last frame of
5643     // the explosion (as it would be done in the following "if"-statement!)
5644     if (border_explosion && phase == last_phase)
5645       return;
5646   }
5647
5648   if (phase == last_phase)
5649   {
5650     int element;
5651
5652     element = Feld[x][y] = Store[x][y];
5653     Store[x][y] = Store2[x][y] = 0;
5654     GfxElement[x][y] = EL_UNDEFINED;
5655
5656     // player can escape from explosions and might therefore be still alive
5657     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5658         element <= EL_PLAYER_IS_EXPLODING_4)
5659     {
5660       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5661       int explosion_element = EL_PLAYER_1 + player_nr;
5662       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5663       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5664
5665       if (level.use_explosion_element[player_nr])
5666         explosion_element = level.explosion_element[player_nr];
5667
5668       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5669                     element_info[explosion_element].content.e[xx][yy]);
5670     }
5671
5672     // restore probably existing indestructible background element
5673     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5674       element = Feld[x][y] = Back[x][y];
5675     Back[x][y] = 0;
5676
5677     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5678     GfxDir[x][y] = MV_NONE;
5679     ChangeDelay[x][y] = 0;
5680     ChangePage[x][y] = -1;
5681
5682     CustomValue[x][y] = 0;
5683
5684     InitField_WithBug2(x, y, FALSE);
5685
5686     TEST_DrawLevelField(x, y);
5687
5688     TestIfElementTouchesCustomElement(x, y);
5689
5690     if (GFX_CRUMBLED(element))
5691       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5692
5693     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5694       StorePlayer[x][y] = 0;
5695
5696     if (ELEM_IS_PLAYER(element))
5697       RelocatePlayer(x, y, element);
5698   }
5699   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5700   {
5701     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5702     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5703
5704     if (phase == delay)
5705       TEST_DrawLevelFieldCrumbled(x, y);
5706
5707     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5708     {
5709       DrawLevelElement(x, y, Back[x][y]);
5710       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5711     }
5712     else if (IS_WALKABLE_UNDER(Back[x][y]))
5713     {
5714       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5715       DrawLevelElementThruMask(x, y, Back[x][y]);
5716     }
5717     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5718       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5719   }
5720 }
5721
5722 static void DynaExplode(int ex, int ey)
5723 {
5724   int i, j;
5725   int dynabomb_element = Feld[ex][ey];
5726   int dynabomb_size = 1;
5727   boolean dynabomb_xl = FALSE;
5728   struct PlayerInfo *player;
5729   static int xy[4][2] =
5730   {
5731     { 0, -1 },
5732     { -1, 0 },
5733     { +1, 0 },
5734     { 0, +1 }
5735   };
5736
5737   if (IS_ACTIVE_BOMB(dynabomb_element))
5738   {
5739     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5740     dynabomb_size = player->dynabomb_size;
5741     dynabomb_xl = player->dynabomb_xl;
5742     player->dynabombs_left++;
5743   }
5744
5745   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5746
5747   for (i = 0; i < NUM_DIRECTIONS; i++)
5748   {
5749     for (j = 1; j <= dynabomb_size; j++)
5750     {
5751       int x = ex + j * xy[i][0];
5752       int y = ey + j * xy[i][1];
5753       int element;
5754
5755       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5756         break;
5757
5758       element = Feld[x][y];
5759
5760       // do not restart explosions of fields with active bombs
5761       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5762         continue;
5763
5764       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5765
5766       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5767           !IS_DIGGABLE(element) && !dynabomb_xl)
5768         break;
5769     }
5770   }
5771 }
5772
5773 void Bang(int x, int y)
5774 {
5775   int element = MovingOrBlocked2Element(x, y);
5776   int explosion_type = EX_TYPE_NORMAL;
5777
5778   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5779   {
5780     struct PlayerInfo *player = PLAYERINFO(x, y);
5781
5782     element = Feld[x][y] = player->initial_element;
5783
5784     if (level.use_explosion_element[player->index_nr])
5785     {
5786       int explosion_element = level.explosion_element[player->index_nr];
5787
5788       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5789         explosion_type = EX_TYPE_CROSS;
5790       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5791         explosion_type = EX_TYPE_CENTER;
5792     }
5793   }
5794
5795   switch (element)
5796   {
5797     case EL_BUG:
5798     case EL_SPACESHIP:
5799     case EL_BD_BUTTERFLY:
5800     case EL_BD_FIREFLY:
5801     case EL_YAMYAM:
5802     case EL_DARK_YAMYAM:
5803     case EL_ROBOT:
5804     case EL_PACMAN:
5805     case EL_MOLE:
5806       RaiseScoreElement(element);
5807       break;
5808
5809     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5810     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5811     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5812     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5813     case EL_DYNABOMB_INCREASE_NUMBER:
5814     case EL_DYNABOMB_INCREASE_SIZE:
5815     case EL_DYNABOMB_INCREASE_POWER:
5816       explosion_type = EX_TYPE_DYNA;
5817       break;
5818
5819     case EL_DC_LANDMINE:
5820       explosion_type = EX_TYPE_CENTER;
5821       break;
5822
5823     case EL_PENGUIN:
5824     case EL_LAMP:
5825     case EL_LAMP_ACTIVE:
5826     case EL_AMOEBA_TO_DIAMOND:
5827       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
5828         explosion_type = EX_TYPE_CENTER;
5829       break;
5830
5831     default:
5832       if (element_info[element].explosion_type == EXPLODES_CROSS)
5833         explosion_type = EX_TYPE_CROSS;
5834       else if (element_info[element].explosion_type == EXPLODES_1X1)
5835         explosion_type = EX_TYPE_CENTER;
5836       break;
5837   }
5838
5839   if (explosion_type == EX_TYPE_DYNA)
5840     DynaExplode(x, y);
5841   else
5842     Explode(x, y, EX_PHASE_START, explosion_type);
5843
5844   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5845 }
5846
5847 static void SplashAcid(int x, int y)
5848 {
5849   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5850       (!IN_LEV_FIELD(x - 1, y - 2) ||
5851        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5852     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5853
5854   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5855       (!IN_LEV_FIELD(x + 1, y - 2) ||
5856        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5857     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5858
5859   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5860 }
5861
5862 static void InitBeltMovement(void)
5863 {
5864   static int belt_base_element[4] =
5865   {
5866     EL_CONVEYOR_BELT_1_LEFT,
5867     EL_CONVEYOR_BELT_2_LEFT,
5868     EL_CONVEYOR_BELT_3_LEFT,
5869     EL_CONVEYOR_BELT_4_LEFT
5870   };
5871   static int belt_base_active_element[4] =
5872   {
5873     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5874     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5875     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5876     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5877   };
5878
5879   int x, y, i, j;
5880
5881   // set frame order for belt animation graphic according to belt direction
5882   for (i = 0; i < NUM_BELTS; i++)
5883   {
5884     int belt_nr = i;
5885
5886     for (j = 0; j < NUM_BELT_PARTS; j++)
5887     {
5888       int element = belt_base_active_element[belt_nr] + j;
5889       int graphic_1 = el2img(element);
5890       int graphic_2 = el2panelimg(element);
5891
5892       if (game.belt_dir[i] == MV_LEFT)
5893       {
5894         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5895         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5896       }
5897       else
5898       {
5899         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5900         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5901       }
5902     }
5903   }
5904
5905   SCAN_PLAYFIELD(x, y)
5906   {
5907     int element = Feld[x][y];
5908
5909     for (i = 0; i < NUM_BELTS; i++)
5910     {
5911       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5912       {
5913         int e_belt_nr = getBeltNrFromBeltElement(element);
5914         int belt_nr = i;
5915
5916         if (e_belt_nr == belt_nr)
5917         {
5918           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5919
5920           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5921         }
5922       }
5923     }
5924   }
5925 }
5926
5927 static void ToggleBeltSwitch(int x, int y)
5928 {
5929   static int belt_base_element[4] =
5930   {
5931     EL_CONVEYOR_BELT_1_LEFT,
5932     EL_CONVEYOR_BELT_2_LEFT,
5933     EL_CONVEYOR_BELT_3_LEFT,
5934     EL_CONVEYOR_BELT_4_LEFT
5935   };
5936   static int belt_base_active_element[4] =
5937   {
5938     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5939     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5940     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5941     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5942   };
5943   static int belt_base_switch_element[4] =
5944   {
5945     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5946     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5947     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5948     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5949   };
5950   static int belt_move_dir[4] =
5951   {
5952     MV_LEFT,
5953     MV_NONE,
5954     MV_RIGHT,
5955     MV_NONE,
5956   };
5957
5958   int element = Feld[x][y];
5959   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5960   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5961   int belt_dir = belt_move_dir[belt_dir_nr];
5962   int xx, yy, i;
5963
5964   if (!IS_BELT_SWITCH(element))
5965     return;
5966
5967   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5968   game.belt_dir[belt_nr] = belt_dir;
5969
5970   if (belt_dir_nr == 3)
5971     belt_dir_nr = 1;
5972
5973   // set frame order for belt animation graphic according to belt direction
5974   for (i = 0; i < NUM_BELT_PARTS; i++)
5975   {
5976     int element = belt_base_active_element[belt_nr] + i;
5977     int graphic_1 = el2img(element);
5978     int graphic_2 = el2panelimg(element);
5979
5980     if (belt_dir == MV_LEFT)
5981     {
5982       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5983       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5984     }
5985     else
5986     {
5987       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5988       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5989     }
5990   }
5991
5992   SCAN_PLAYFIELD(xx, yy)
5993   {
5994     int element = Feld[xx][yy];
5995
5996     if (IS_BELT_SWITCH(element))
5997     {
5998       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5999
6000       if (e_belt_nr == belt_nr)
6001       {
6002         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6003         TEST_DrawLevelField(xx, yy);
6004       }
6005     }
6006     else if (IS_BELT(element) && belt_dir != MV_NONE)
6007     {
6008       int e_belt_nr = getBeltNrFromBeltElement(element);
6009
6010       if (e_belt_nr == belt_nr)
6011       {
6012         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6013
6014         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6015         TEST_DrawLevelField(xx, yy);
6016       }
6017     }
6018     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6019     {
6020       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6021
6022       if (e_belt_nr == belt_nr)
6023       {
6024         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6025
6026         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6027         TEST_DrawLevelField(xx, yy);
6028       }
6029     }
6030   }
6031 }
6032
6033 static void ToggleSwitchgateSwitch(int x, int y)
6034 {
6035   int xx, yy;
6036
6037   game.switchgate_pos = !game.switchgate_pos;
6038
6039   SCAN_PLAYFIELD(xx, yy)
6040   {
6041     int element = Feld[xx][yy];
6042
6043     if (element == EL_SWITCHGATE_SWITCH_UP)
6044     {
6045       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6046       TEST_DrawLevelField(xx, yy);
6047     }
6048     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6049     {
6050       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6051       TEST_DrawLevelField(xx, yy);
6052     }
6053     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6054     {
6055       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6056       TEST_DrawLevelField(xx, yy);
6057     }
6058     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6059     {
6060       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6061       TEST_DrawLevelField(xx, yy);
6062     }
6063     else if (element == EL_SWITCHGATE_OPEN ||
6064              element == EL_SWITCHGATE_OPENING)
6065     {
6066       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6067
6068       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6069     }
6070     else if (element == EL_SWITCHGATE_CLOSED ||
6071              element == EL_SWITCHGATE_CLOSING)
6072     {
6073       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6074
6075       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6076     }
6077   }
6078 }
6079
6080 static int getInvisibleActiveFromInvisibleElement(int element)
6081 {
6082   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6083           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6084           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6085           element);
6086 }
6087
6088 static int getInvisibleFromInvisibleActiveElement(int element)
6089 {
6090   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6091           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6092           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6093           element);
6094 }
6095
6096 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6097 {
6098   int x, y;
6099
6100   SCAN_PLAYFIELD(x, y)
6101   {
6102     int element = Feld[x][y];
6103
6104     if (element == EL_LIGHT_SWITCH &&
6105         game.light_time_left > 0)
6106     {
6107       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6108       TEST_DrawLevelField(x, y);
6109     }
6110     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6111              game.light_time_left == 0)
6112     {
6113       Feld[x][y] = EL_LIGHT_SWITCH;
6114       TEST_DrawLevelField(x, y);
6115     }
6116     else if (element == EL_EMC_DRIPPER &&
6117              game.light_time_left > 0)
6118     {
6119       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6120       TEST_DrawLevelField(x, y);
6121     }
6122     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6123              game.light_time_left == 0)
6124     {
6125       Feld[x][y] = EL_EMC_DRIPPER;
6126       TEST_DrawLevelField(x, y);
6127     }
6128     else if (element == EL_INVISIBLE_STEELWALL ||
6129              element == EL_INVISIBLE_WALL ||
6130              element == EL_INVISIBLE_SAND)
6131     {
6132       if (game.light_time_left > 0)
6133         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6134
6135       TEST_DrawLevelField(x, y);
6136
6137       // uncrumble neighbour fields, if needed
6138       if (element == EL_INVISIBLE_SAND)
6139         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6140     }
6141     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6142              element == EL_INVISIBLE_WALL_ACTIVE ||
6143              element == EL_INVISIBLE_SAND_ACTIVE)
6144     {
6145       if (game.light_time_left == 0)
6146         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6147
6148       TEST_DrawLevelField(x, y);
6149
6150       // re-crumble neighbour fields, if needed
6151       if (element == EL_INVISIBLE_SAND)
6152         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6153     }
6154   }
6155 }
6156
6157 static void RedrawAllInvisibleElementsForLenses(void)
6158 {
6159   int x, y;
6160
6161   SCAN_PLAYFIELD(x, y)
6162   {
6163     int element = Feld[x][y];
6164
6165     if (element == EL_EMC_DRIPPER &&
6166         game.lenses_time_left > 0)
6167     {
6168       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6169       TEST_DrawLevelField(x, y);
6170     }
6171     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6172              game.lenses_time_left == 0)
6173     {
6174       Feld[x][y] = EL_EMC_DRIPPER;
6175       TEST_DrawLevelField(x, y);
6176     }
6177     else if (element == EL_INVISIBLE_STEELWALL ||
6178              element == EL_INVISIBLE_WALL ||
6179              element == EL_INVISIBLE_SAND)
6180     {
6181       if (game.lenses_time_left > 0)
6182         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6183
6184       TEST_DrawLevelField(x, y);
6185
6186       // uncrumble neighbour fields, if needed
6187       if (element == EL_INVISIBLE_SAND)
6188         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6189     }
6190     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6191              element == EL_INVISIBLE_WALL_ACTIVE ||
6192              element == EL_INVISIBLE_SAND_ACTIVE)
6193     {
6194       if (game.lenses_time_left == 0)
6195         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6196
6197       TEST_DrawLevelField(x, y);
6198
6199       // re-crumble neighbour fields, if needed
6200       if (element == EL_INVISIBLE_SAND)
6201         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6202     }
6203   }
6204 }
6205
6206 static void RedrawAllInvisibleElementsForMagnifier(void)
6207 {
6208   int x, y;
6209
6210   SCAN_PLAYFIELD(x, y)
6211   {
6212     int element = Feld[x][y];
6213
6214     if (element == EL_EMC_FAKE_GRASS &&
6215         game.magnify_time_left > 0)
6216     {
6217       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6218       TEST_DrawLevelField(x, y);
6219     }
6220     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6221              game.magnify_time_left == 0)
6222     {
6223       Feld[x][y] = EL_EMC_FAKE_GRASS;
6224       TEST_DrawLevelField(x, y);
6225     }
6226     else if (IS_GATE_GRAY(element) &&
6227              game.magnify_time_left > 0)
6228     {
6229       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6230                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6231                     IS_EM_GATE_GRAY(element) ?
6232                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6233                     IS_EMC_GATE_GRAY(element) ?
6234                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6235                     IS_DC_GATE_GRAY(element) ?
6236                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6237                     element);
6238       TEST_DrawLevelField(x, y);
6239     }
6240     else if (IS_GATE_GRAY_ACTIVE(element) &&
6241              game.magnify_time_left == 0)
6242     {
6243       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6244                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6245                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6246                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6247                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6248                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6249                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6250                     EL_DC_GATE_WHITE_GRAY :
6251                     element);
6252       TEST_DrawLevelField(x, y);
6253     }
6254   }
6255 }
6256
6257 static void ToggleLightSwitch(int x, int y)
6258 {
6259   int element = Feld[x][y];
6260
6261   game.light_time_left =
6262     (element == EL_LIGHT_SWITCH ?
6263      level.time_light * FRAMES_PER_SECOND : 0);
6264
6265   RedrawAllLightSwitchesAndInvisibleElements();
6266 }
6267
6268 static void ActivateTimegateSwitch(int x, int y)
6269 {
6270   int xx, yy;
6271
6272   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6273
6274   SCAN_PLAYFIELD(xx, yy)
6275   {
6276     int element = Feld[xx][yy];
6277
6278     if (element == EL_TIMEGATE_CLOSED ||
6279         element == EL_TIMEGATE_CLOSING)
6280     {
6281       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6282       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6283     }
6284
6285     /*
6286     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6287     {
6288       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6289       TEST_DrawLevelField(xx, yy);
6290     }
6291     */
6292
6293   }
6294
6295   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6296                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6297 }
6298
6299 static void Impact(int x, int y)
6300 {
6301   boolean last_line = (y == lev_fieldy - 1);
6302   boolean object_hit = FALSE;
6303   boolean impact = (last_line || object_hit);
6304   int element = Feld[x][y];
6305   int smashed = EL_STEELWALL;
6306
6307   if (!last_line)       // check if element below was hit
6308   {
6309     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6310       return;
6311
6312     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6313                                          MovDir[x][y + 1] != MV_DOWN ||
6314                                          MovPos[x][y + 1] <= TILEY / 2));
6315
6316     // do not smash moving elements that left the smashed field in time
6317     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6318         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6319       object_hit = FALSE;
6320
6321 #if USE_QUICKSAND_IMPACT_BUGFIX
6322     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6323     {
6324       RemoveMovingField(x, y + 1);
6325       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6326       Feld[x][y + 2] = EL_ROCK;
6327       TEST_DrawLevelField(x, y + 2);
6328
6329       object_hit = TRUE;
6330     }
6331
6332     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6333     {
6334       RemoveMovingField(x, y + 1);
6335       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6336       Feld[x][y + 2] = EL_ROCK;
6337       TEST_DrawLevelField(x, y + 2);
6338
6339       object_hit = TRUE;
6340     }
6341 #endif
6342
6343     if (object_hit)
6344       smashed = MovingOrBlocked2Element(x, y + 1);
6345
6346     impact = (last_line || object_hit);
6347   }
6348
6349   if (!last_line && smashed == EL_ACID) // element falls into acid
6350   {
6351     SplashAcid(x, y + 1);
6352     return;
6353   }
6354
6355   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6356   // only reset graphic animation if graphic really changes after impact
6357   if (impact &&
6358       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6359   {
6360     ResetGfxAnimation(x, y);
6361     TEST_DrawLevelField(x, y);
6362   }
6363
6364   if (impact && CAN_EXPLODE_IMPACT(element))
6365   {
6366     Bang(x, y);
6367     return;
6368   }
6369   else if (impact && element == EL_PEARL &&
6370            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6371   {
6372     ResetGfxAnimation(x, y);
6373
6374     Feld[x][y] = EL_PEARL_BREAKING;
6375     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6376     return;
6377   }
6378   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6379   {
6380     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6381
6382     return;
6383   }
6384
6385   if (impact && element == EL_AMOEBA_DROP)
6386   {
6387     if (object_hit && IS_PLAYER(x, y + 1))
6388       KillPlayerUnlessEnemyProtected(x, y + 1);
6389     else if (object_hit && smashed == EL_PENGUIN)
6390       Bang(x, y + 1);
6391     else
6392     {
6393       Feld[x][y] = EL_AMOEBA_GROWING;
6394       Store[x][y] = EL_AMOEBA_WET;
6395
6396       ResetRandomAnimationValue(x, y);
6397     }
6398     return;
6399   }
6400
6401   if (object_hit)               // check which object was hit
6402   {
6403     if ((CAN_PASS_MAGIC_WALL(element) && 
6404          (smashed == EL_MAGIC_WALL ||
6405           smashed == EL_BD_MAGIC_WALL)) ||
6406         (CAN_PASS_DC_MAGIC_WALL(element) &&
6407          smashed == EL_DC_MAGIC_WALL))
6408     {
6409       int xx, yy;
6410       int activated_magic_wall =
6411         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6412          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6413          EL_DC_MAGIC_WALL_ACTIVE);
6414
6415       // activate magic wall / mill
6416       SCAN_PLAYFIELD(xx, yy)
6417       {
6418         if (Feld[xx][yy] == smashed)
6419           Feld[xx][yy] = activated_magic_wall;
6420       }
6421
6422       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6423       game.magic_wall_active = TRUE;
6424
6425       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6426                             SND_MAGIC_WALL_ACTIVATING :
6427                             smashed == EL_BD_MAGIC_WALL ?
6428                             SND_BD_MAGIC_WALL_ACTIVATING :
6429                             SND_DC_MAGIC_WALL_ACTIVATING));
6430     }
6431
6432     if (IS_PLAYER(x, y + 1))
6433     {
6434       if (CAN_SMASH_PLAYER(element))
6435       {
6436         KillPlayerUnlessEnemyProtected(x, y + 1);
6437         return;
6438       }
6439     }
6440     else if (smashed == EL_PENGUIN)
6441     {
6442       if (CAN_SMASH_PLAYER(element))
6443       {
6444         Bang(x, y + 1);
6445         return;
6446       }
6447     }
6448     else if (element == EL_BD_DIAMOND)
6449     {
6450       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6451       {
6452         Bang(x, y + 1);
6453         return;
6454       }
6455     }
6456     else if (((element == EL_SP_INFOTRON ||
6457                element == EL_SP_ZONK) &&
6458               (smashed == EL_SP_SNIKSNAK ||
6459                smashed == EL_SP_ELECTRON ||
6460                smashed == EL_SP_DISK_ORANGE)) ||
6461              (element == EL_SP_INFOTRON &&
6462               smashed == EL_SP_DISK_YELLOW))
6463     {
6464       Bang(x, y + 1);
6465       return;
6466     }
6467     else if (CAN_SMASH_EVERYTHING(element))
6468     {
6469       if (IS_CLASSIC_ENEMY(smashed) ||
6470           CAN_EXPLODE_SMASHED(smashed))
6471       {
6472         Bang(x, y + 1);
6473         return;
6474       }
6475       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6476       {
6477         if (smashed == EL_LAMP ||
6478             smashed == EL_LAMP_ACTIVE)
6479         {
6480           Bang(x, y + 1);
6481           return;
6482         }
6483         else if (smashed == EL_NUT)
6484         {
6485           Feld[x][y + 1] = EL_NUT_BREAKING;
6486           PlayLevelSound(x, y, SND_NUT_BREAKING);
6487           RaiseScoreElement(EL_NUT);
6488           return;
6489         }
6490         else if (smashed == EL_PEARL)
6491         {
6492           ResetGfxAnimation(x, y);
6493
6494           Feld[x][y + 1] = EL_PEARL_BREAKING;
6495           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6496           return;
6497         }
6498         else if (smashed == EL_DIAMOND)
6499         {
6500           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6501           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6502           return;
6503         }
6504         else if (IS_BELT_SWITCH(smashed))
6505         {
6506           ToggleBeltSwitch(x, y + 1);
6507         }
6508         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6509                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6510                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6511                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6512         {
6513           ToggleSwitchgateSwitch(x, y + 1);
6514         }
6515         else if (smashed == EL_LIGHT_SWITCH ||
6516                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6517         {
6518           ToggleLightSwitch(x, y + 1);
6519         }
6520         else
6521         {
6522           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6523
6524           CheckElementChangeBySide(x, y + 1, smashed, element,
6525                                    CE_SWITCHED, CH_SIDE_TOP);
6526           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6527                                             CH_SIDE_TOP);
6528         }
6529       }
6530       else
6531       {
6532         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6533       }
6534     }
6535   }
6536
6537   // play sound of magic wall / mill
6538   if (!last_line &&
6539       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6540        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6541        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6542   {
6543     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6544       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6545     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6546       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6547     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6548       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6549
6550     return;
6551   }
6552
6553   // play sound of object that hits the ground
6554   if (last_line || object_hit)
6555     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6556 }
6557
6558 static void TurnRoundExt(int x, int y)
6559 {
6560   static struct
6561   {
6562     int dx, dy;
6563   } move_xy[] =
6564   {
6565     {  0,  0 },
6566     { -1,  0 },
6567     { +1,  0 },
6568     {  0,  0 },
6569     {  0, -1 },
6570     {  0,  0 }, { 0, 0 }, { 0, 0 },
6571     {  0, +1 }
6572   };
6573   static struct
6574   {
6575     int left, right, back;
6576   } turn[] =
6577   {
6578     { 0,        0,              0        },
6579     { MV_DOWN,  MV_UP,          MV_RIGHT },
6580     { MV_UP,    MV_DOWN,        MV_LEFT  },
6581     { 0,        0,              0        },
6582     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6583     { 0,        0,              0        },
6584     { 0,        0,              0        },
6585     { 0,        0,              0        },
6586     { MV_RIGHT, MV_LEFT,        MV_UP    }
6587   };
6588
6589   int element = Feld[x][y];
6590   int move_pattern = element_info[element].move_pattern;
6591
6592   int old_move_dir = MovDir[x][y];
6593   int left_dir  = turn[old_move_dir].left;
6594   int right_dir = turn[old_move_dir].right;
6595   int back_dir  = turn[old_move_dir].back;
6596
6597   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6598   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6599   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6600   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6601
6602   int left_x  = x + left_dx,  left_y  = y + left_dy;
6603   int right_x = x + right_dx, right_y = y + right_dy;
6604   int move_x  = x + move_dx,  move_y  = y + move_dy;
6605
6606   int xx, yy;
6607
6608   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6609   {
6610     TestIfBadThingTouchesOtherBadThing(x, y);
6611
6612     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6613       MovDir[x][y] = right_dir;
6614     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6615       MovDir[x][y] = left_dir;
6616
6617     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6618       MovDelay[x][y] = 9;
6619     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6620       MovDelay[x][y] = 1;
6621   }
6622   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6623   {
6624     TestIfBadThingTouchesOtherBadThing(x, y);
6625
6626     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6627       MovDir[x][y] = left_dir;
6628     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6629       MovDir[x][y] = right_dir;
6630
6631     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6632       MovDelay[x][y] = 9;
6633     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6634       MovDelay[x][y] = 1;
6635   }
6636   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6637   {
6638     TestIfBadThingTouchesOtherBadThing(x, y);
6639
6640     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6641       MovDir[x][y] = left_dir;
6642     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6643       MovDir[x][y] = right_dir;
6644
6645     if (MovDir[x][y] != old_move_dir)
6646       MovDelay[x][y] = 9;
6647   }
6648   else if (element == EL_YAMYAM)
6649   {
6650     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6651     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6652
6653     if (can_turn_left && can_turn_right)
6654       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6655     else if (can_turn_left)
6656       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6657     else if (can_turn_right)
6658       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6659     else
6660       MovDir[x][y] = back_dir;
6661
6662     MovDelay[x][y] = 16 + 16 * RND(3);
6663   }
6664   else if (element == EL_DARK_YAMYAM)
6665   {
6666     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6667                                                          left_x, left_y);
6668     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6669                                                          right_x, right_y);
6670
6671     if (can_turn_left && can_turn_right)
6672       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6673     else if (can_turn_left)
6674       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6675     else if (can_turn_right)
6676       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6677     else
6678       MovDir[x][y] = back_dir;
6679
6680     MovDelay[x][y] = 16 + 16 * RND(3);
6681   }
6682   else if (element == EL_PACMAN)
6683   {
6684     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6685     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6686
6687     if (can_turn_left && can_turn_right)
6688       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6689     else if (can_turn_left)
6690       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6691     else if (can_turn_right)
6692       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6693     else
6694       MovDir[x][y] = back_dir;
6695
6696     MovDelay[x][y] = 6 + RND(40);
6697   }
6698   else if (element == EL_PIG)
6699   {
6700     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6701     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6702     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6703     boolean should_turn_left, should_turn_right, should_move_on;
6704     int rnd_value = 24;
6705     int rnd = RND(rnd_value);
6706
6707     should_turn_left = (can_turn_left &&
6708                         (!can_move_on ||
6709                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6710                                                    y + back_dy + left_dy)));
6711     should_turn_right = (can_turn_right &&
6712                          (!can_move_on ||
6713                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6714                                                     y + back_dy + right_dy)));
6715     should_move_on = (can_move_on &&
6716                       (!can_turn_left ||
6717                        !can_turn_right ||
6718                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6719                                                  y + move_dy + left_dy) ||
6720                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6721                                                  y + move_dy + right_dy)));
6722
6723     if (should_turn_left || should_turn_right || should_move_on)
6724     {
6725       if (should_turn_left && should_turn_right && should_move_on)
6726         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6727                         rnd < 2 * rnd_value / 3 ? right_dir :
6728                         old_move_dir);
6729       else if (should_turn_left && should_turn_right)
6730         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6731       else if (should_turn_left && should_move_on)
6732         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6733       else if (should_turn_right && should_move_on)
6734         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6735       else if (should_turn_left)
6736         MovDir[x][y] = left_dir;
6737       else if (should_turn_right)
6738         MovDir[x][y] = right_dir;
6739       else if (should_move_on)
6740         MovDir[x][y] = old_move_dir;
6741     }
6742     else if (can_move_on && rnd > rnd_value / 8)
6743       MovDir[x][y] = old_move_dir;
6744     else if (can_turn_left && can_turn_right)
6745       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6746     else if (can_turn_left && rnd > rnd_value / 8)
6747       MovDir[x][y] = left_dir;
6748     else if (can_turn_right && rnd > rnd_value/8)
6749       MovDir[x][y] = right_dir;
6750     else
6751       MovDir[x][y] = back_dir;
6752
6753     xx = x + move_xy[MovDir[x][y]].dx;
6754     yy = y + move_xy[MovDir[x][y]].dy;
6755
6756     if (!IN_LEV_FIELD(xx, yy) ||
6757         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6758       MovDir[x][y] = old_move_dir;
6759
6760     MovDelay[x][y] = 0;
6761   }
6762   else if (element == EL_DRAGON)
6763   {
6764     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6765     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6766     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6767     int rnd_value = 24;
6768     int rnd = RND(rnd_value);
6769
6770     if (can_move_on && rnd > rnd_value / 8)
6771       MovDir[x][y] = old_move_dir;
6772     else if (can_turn_left && can_turn_right)
6773       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6774     else if (can_turn_left && rnd > rnd_value / 8)
6775       MovDir[x][y] = left_dir;
6776     else if (can_turn_right && rnd > rnd_value / 8)
6777       MovDir[x][y] = right_dir;
6778     else
6779       MovDir[x][y] = back_dir;
6780
6781     xx = x + move_xy[MovDir[x][y]].dx;
6782     yy = y + move_xy[MovDir[x][y]].dy;
6783
6784     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6785       MovDir[x][y] = old_move_dir;
6786
6787     MovDelay[x][y] = 0;
6788   }
6789   else if (element == EL_MOLE)
6790   {
6791     boolean can_move_on =
6792       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6793                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6794                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6795     if (!can_move_on)
6796     {
6797       boolean can_turn_left =
6798         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6799                               IS_AMOEBOID(Feld[left_x][left_y])));
6800
6801       boolean can_turn_right =
6802         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6803                               IS_AMOEBOID(Feld[right_x][right_y])));
6804
6805       if (can_turn_left && can_turn_right)
6806         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6807       else if (can_turn_left)
6808         MovDir[x][y] = left_dir;
6809       else
6810         MovDir[x][y] = right_dir;
6811     }
6812
6813     if (MovDir[x][y] != old_move_dir)
6814       MovDelay[x][y] = 9;
6815   }
6816   else if (element == EL_BALLOON)
6817   {
6818     MovDir[x][y] = game.wind_direction;
6819     MovDelay[x][y] = 0;
6820   }
6821   else if (element == EL_SPRING)
6822   {
6823     if (MovDir[x][y] & MV_HORIZONTAL)
6824     {
6825       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6826           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6827       {
6828         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6829         ResetGfxAnimation(move_x, move_y);
6830         TEST_DrawLevelField(move_x, move_y);
6831
6832         MovDir[x][y] = back_dir;
6833       }
6834       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6835                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6836         MovDir[x][y] = MV_NONE;
6837     }
6838
6839     MovDelay[x][y] = 0;
6840   }
6841   else if (element == EL_ROBOT ||
6842            element == EL_SATELLITE ||
6843            element == EL_PENGUIN ||
6844            element == EL_EMC_ANDROID)
6845   {
6846     int attr_x = -1, attr_y = -1;
6847
6848     if (game.all_players_gone)
6849     {
6850       attr_x = game.exit_x;
6851       attr_y = game.exit_y;
6852     }
6853     else
6854     {
6855       int i;
6856
6857       for (i = 0; i < MAX_PLAYERS; i++)
6858       {
6859         struct PlayerInfo *player = &stored_player[i];
6860         int jx = player->jx, jy = player->jy;
6861
6862         if (!player->active)
6863           continue;
6864
6865         if (attr_x == -1 ||
6866             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6867         {
6868           attr_x = jx;
6869           attr_y = jy;
6870         }
6871       }
6872     }
6873
6874     if (element == EL_ROBOT &&
6875         game.robot_wheel_x >= 0 &&
6876         game.robot_wheel_y >= 0 &&
6877         (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
6878          game.engine_version < VERSION_IDENT(3,1,0,0)))
6879     {
6880       attr_x = game.robot_wheel_x;
6881       attr_y = game.robot_wheel_y;
6882     }
6883
6884     if (element == EL_PENGUIN)
6885     {
6886       int i;
6887       static int xy[4][2] =
6888       {
6889         { 0, -1 },
6890         { -1, 0 },
6891         { +1, 0 },
6892         { 0, +1 }
6893       };
6894
6895       for (i = 0; i < NUM_DIRECTIONS; i++)
6896       {
6897         int ex = x + xy[i][0];
6898         int ey = y + xy[i][1];
6899
6900         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6901                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6902                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6903                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6904         {
6905           attr_x = ex;
6906           attr_y = ey;
6907           break;
6908         }
6909       }
6910     }
6911
6912     MovDir[x][y] = MV_NONE;
6913     if (attr_x < x)
6914       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
6915     else if (attr_x > x)
6916       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
6917     if (attr_y < y)
6918       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
6919     else if (attr_y > y)
6920       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
6921
6922     if (element == EL_ROBOT)
6923     {
6924       int newx, newy;
6925
6926       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6927         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6928       Moving2Blocked(x, y, &newx, &newy);
6929
6930       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6931         MovDelay[x][y] = 8 + 8 * !RND(3);
6932       else
6933         MovDelay[x][y] = 16;
6934     }
6935     else if (element == EL_PENGUIN)
6936     {
6937       int newx, newy;
6938
6939       MovDelay[x][y] = 1;
6940
6941       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6942       {
6943         boolean first_horiz = RND(2);
6944         int new_move_dir = MovDir[x][y];
6945
6946         MovDir[x][y] =
6947           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6948         Moving2Blocked(x, y, &newx, &newy);
6949
6950         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6951           return;
6952
6953         MovDir[x][y] =
6954           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6955         Moving2Blocked(x, y, &newx, &newy);
6956
6957         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6958           return;
6959
6960         MovDir[x][y] = old_move_dir;
6961         return;
6962       }
6963     }
6964     else if (element == EL_SATELLITE)
6965     {
6966       int newx, newy;
6967
6968       MovDelay[x][y] = 1;
6969
6970       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6971       {
6972         boolean first_horiz = RND(2);
6973         int new_move_dir = MovDir[x][y];
6974
6975         MovDir[x][y] =
6976           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6977         Moving2Blocked(x, y, &newx, &newy);
6978
6979         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6980           return;
6981
6982         MovDir[x][y] =
6983           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6984         Moving2Blocked(x, y, &newx, &newy);
6985
6986         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6987           return;
6988
6989         MovDir[x][y] = old_move_dir;
6990         return;
6991       }
6992     }
6993     else if (element == EL_EMC_ANDROID)
6994     {
6995       static int check_pos[16] =
6996       {
6997         -1,             //  0 => (invalid)
6998         7,              //  1 => MV_LEFT
6999         3,              //  2 => MV_RIGHT
7000         -1,             //  3 => (invalid)
7001         1,              //  4 =>            MV_UP
7002         0,              //  5 => MV_LEFT  | MV_UP
7003         2,              //  6 => MV_RIGHT | MV_UP
7004         -1,             //  7 => (invalid)
7005         5,              //  8 =>            MV_DOWN
7006         6,              //  9 => MV_LEFT  | MV_DOWN
7007         4,              // 10 => MV_RIGHT | MV_DOWN
7008         -1,             // 11 => (invalid)
7009         -1,             // 12 => (invalid)
7010         -1,             // 13 => (invalid)
7011         -1,             // 14 => (invalid)
7012         -1,             // 15 => (invalid)
7013       };
7014       static struct
7015       {
7016         int dx, dy;
7017         int dir;
7018       } check_xy[8] =
7019       {
7020         { -1, -1,       MV_LEFT  | MV_UP   },
7021         {  0, -1,                  MV_UP   },
7022         { +1, -1,       MV_RIGHT | MV_UP   },
7023         { +1,  0,       MV_RIGHT           },
7024         { +1, +1,       MV_RIGHT | MV_DOWN },
7025         {  0, +1,                  MV_DOWN },
7026         { -1, +1,       MV_LEFT  | MV_DOWN },
7027         { -1,  0,       MV_LEFT            },
7028       };
7029       int start_pos, check_order;
7030       boolean can_clone = FALSE;
7031       int i;
7032
7033       // check if there is any free field around current position
7034       for (i = 0; i < 8; i++)
7035       {
7036         int newx = x + check_xy[i].dx;
7037         int newy = y + check_xy[i].dy;
7038
7039         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7040         {
7041           can_clone = TRUE;
7042
7043           break;
7044         }
7045       }
7046
7047       if (can_clone)            // randomly find an element to clone
7048       {
7049         can_clone = FALSE;
7050
7051         start_pos = check_pos[RND(8)];
7052         check_order = (RND(2) ? -1 : +1);
7053
7054         for (i = 0; i < 8; i++)
7055         {
7056           int pos_raw = start_pos + i * check_order;
7057           int pos = (pos_raw + 8) % 8;
7058           int newx = x + check_xy[pos].dx;
7059           int newy = y + check_xy[pos].dy;
7060
7061           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7062           {
7063             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7064             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7065
7066             Store[x][y] = Feld[newx][newy];
7067
7068             can_clone = TRUE;
7069
7070             break;
7071           }
7072         }
7073       }
7074
7075       if (can_clone)            // randomly find a direction to move
7076       {
7077         can_clone = FALSE;
7078
7079         start_pos = check_pos[RND(8)];
7080         check_order = (RND(2) ? -1 : +1);
7081
7082         for (i = 0; i < 8; i++)
7083         {
7084           int pos_raw = start_pos + i * check_order;
7085           int pos = (pos_raw + 8) % 8;
7086           int newx = x + check_xy[pos].dx;
7087           int newy = y + check_xy[pos].dy;
7088           int new_move_dir = check_xy[pos].dir;
7089
7090           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7091           {
7092             MovDir[x][y] = new_move_dir;
7093             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7094
7095             can_clone = TRUE;
7096
7097             break;
7098           }
7099         }
7100       }
7101
7102       if (can_clone)            // cloning and moving successful
7103         return;
7104
7105       // cannot clone -- try to move towards player
7106
7107       start_pos = check_pos[MovDir[x][y] & 0x0f];
7108       check_order = (RND(2) ? -1 : +1);
7109
7110       for (i = 0; i < 3; i++)
7111       {
7112         // first check start_pos, then previous/next or (next/previous) pos
7113         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7114         int pos = (pos_raw + 8) % 8;
7115         int newx = x + check_xy[pos].dx;
7116         int newy = y + check_xy[pos].dy;
7117         int new_move_dir = check_xy[pos].dir;
7118
7119         if (IS_PLAYER(newx, newy))
7120           break;
7121
7122         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7123         {
7124           MovDir[x][y] = new_move_dir;
7125           MovDelay[x][y] = level.android_move_time * 8 + 1;
7126
7127           break;
7128         }
7129       }
7130     }
7131   }
7132   else if (move_pattern == MV_TURNING_LEFT ||
7133            move_pattern == MV_TURNING_RIGHT ||
7134            move_pattern == MV_TURNING_LEFT_RIGHT ||
7135            move_pattern == MV_TURNING_RIGHT_LEFT ||
7136            move_pattern == MV_TURNING_RANDOM ||
7137            move_pattern == MV_ALL_DIRECTIONS)
7138   {
7139     boolean can_turn_left =
7140       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7141     boolean can_turn_right =
7142       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7143
7144     if (element_info[element].move_stepsize == 0)       // "not moving"
7145       return;
7146
7147     if (move_pattern == MV_TURNING_LEFT)
7148       MovDir[x][y] = left_dir;
7149     else if (move_pattern == MV_TURNING_RIGHT)
7150       MovDir[x][y] = right_dir;
7151     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7152       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7153     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7154       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7155     else if (move_pattern == MV_TURNING_RANDOM)
7156       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7157                       can_turn_right && !can_turn_left ? right_dir :
7158                       RND(2) ? left_dir : right_dir);
7159     else if (can_turn_left && can_turn_right)
7160       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7161     else if (can_turn_left)
7162       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7163     else if (can_turn_right)
7164       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7165     else
7166       MovDir[x][y] = back_dir;
7167
7168     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7169   }
7170   else if (move_pattern == MV_HORIZONTAL ||
7171            move_pattern == MV_VERTICAL)
7172   {
7173     if (move_pattern & old_move_dir)
7174       MovDir[x][y] = back_dir;
7175     else if (move_pattern == MV_HORIZONTAL)
7176       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7177     else if (move_pattern == MV_VERTICAL)
7178       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7179
7180     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7181   }
7182   else if (move_pattern & MV_ANY_DIRECTION)
7183   {
7184     MovDir[x][y] = move_pattern;
7185     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7186   }
7187   else if (move_pattern & MV_WIND_DIRECTION)
7188   {
7189     MovDir[x][y] = game.wind_direction;
7190     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7191   }
7192   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7193   {
7194     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7195       MovDir[x][y] = left_dir;
7196     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7197       MovDir[x][y] = right_dir;
7198
7199     if (MovDir[x][y] != old_move_dir)
7200       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7201   }
7202   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7203   {
7204     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7205       MovDir[x][y] = right_dir;
7206     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7207       MovDir[x][y] = left_dir;
7208
7209     if (MovDir[x][y] != old_move_dir)
7210       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7211   }
7212   else if (move_pattern == MV_TOWARDS_PLAYER ||
7213            move_pattern == MV_AWAY_FROM_PLAYER)
7214   {
7215     int attr_x = -1, attr_y = -1;
7216     int newx, newy;
7217     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7218
7219     if (game.all_players_gone)
7220     {
7221       attr_x = game.exit_x;
7222       attr_y = game.exit_y;
7223     }
7224     else
7225     {
7226       int i;
7227
7228       for (i = 0; i < MAX_PLAYERS; i++)
7229       {
7230         struct PlayerInfo *player = &stored_player[i];
7231         int jx = player->jx, jy = player->jy;
7232
7233         if (!player->active)
7234           continue;
7235
7236         if (attr_x == -1 ||
7237             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7238         {
7239           attr_x = jx;
7240           attr_y = jy;
7241         }
7242       }
7243     }
7244
7245     MovDir[x][y] = MV_NONE;
7246     if (attr_x < x)
7247       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7248     else if (attr_x > x)
7249       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7250     if (attr_y < y)
7251       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7252     else if (attr_y > y)
7253       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7254
7255     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7256
7257     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7258     {
7259       boolean first_horiz = RND(2);
7260       int new_move_dir = MovDir[x][y];
7261
7262       if (element_info[element].move_stepsize == 0)     // "not moving"
7263       {
7264         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7265         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7266
7267         return;
7268       }
7269
7270       MovDir[x][y] =
7271         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7272       Moving2Blocked(x, y, &newx, &newy);
7273
7274       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7275         return;
7276
7277       MovDir[x][y] =
7278         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7279       Moving2Blocked(x, y, &newx, &newy);
7280
7281       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7282         return;
7283
7284       MovDir[x][y] = old_move_dir;
7285     }
7286   }
7287   else if (move_pattern == MV_WHEN_PUSHED ||
7288            move_pattern == MV_WHEN_DROPPED)
7289   {
7290     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7291       MovDir[x][y] = MV_NONE;
7292
7293     MovDelay[x][y] = 0;
7294   }
7295   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7296   {
7297     static int test_xy[7][2] =
7298     {
7299       { 0, -1 },
7300       { -1, 0 },
7301       { +1, 0 },
7302       { 0, +1 },
7303       { 0, -1 },
7304       { -1, 0 },
7305       { +1, 0 },
7306     };
7307     static int test_dir[7] =
7308     {
7309       MV_UP,
7310       MV_LEFT,
7311       MV_RIGHT,
7312       MV_DOWN,
7313       MV_UP,
7314       MV_LEFT,
7315       MV_RIGHT,
7316     };
7317     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7318     int move_preference = -1000000;     // start with very low preference
7319     int new_move_dir = MV_NONE;
7320     int start_test = RND(4);
7321     int i;
7322
7323     for (i = 0; i < NUM_DIRECTIONS; i++)
7324     {
7325       int move_dir = test_dir[start_test + i];
7326       int move_dir_preference;
7327
7328       xx = x + test_xy[start_test + i][0];
7329       yy = y + test_xy[start_test + i][1];
7330
7331       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7332           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7333       {
7334         new_move_dir = move_dir;
7335
7336         break;
7337       }
7338
7339       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7340         continue;
7341
7342       move_dir_preference = -1 * RunnerVisit[xx][yy];
7343       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7344         move_dir_preference = PlayerVisit[xx][yy];
7345
7346       if (move_dir_preference > move_preference)
7347       {
7348         // prefer field that has not been visited for the longest time
7349         move_preference = move_dir_preference;
7350         new_move_dir = move_dir;
7351       }
7352       else if (move_dir_preference == move_preference &&
7353                move_dir == old_move_dir)
7354       {
7355         // prefer last direction when all directions are preferred equally
7356         move_preference = move_dir_preference;
7357         new_move_dir = move_dir;
7358       }
7359     }
7360
7361     MovDir[x][y] = new_move_dir;
7362     if (old_move_dir != new_move_dir)
7363       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7364   }
7365 }
7366
7367 static void TurnRound(int x, int y)
7368 {
7369   int direction = MovDir[x][y];
7370
7371   TurnRoundExt(x, y);
7372
7373   GfxDir[x][y] = MovDir[x][y];
7374
7375   if (direction != MovDir[x][y])
7376     GfxFrame[x][y] = 0;
7377
7378   if (MovDelay[x][y])
7379     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7380
7381   ResetGfxFrame(x, y);
7382 }
7383
7384 static boolean JustBeingPushed(int x, int y)
7385 {
7386   int i;
7387
7388   for (i = 0; i < MAX_PLAYERS; i++)
7389   {
7390     struct PlayerInfo *player = &stored_player[i];
7391
7392     if (player->active && player->is_pushing && player->MovPos)
7393     {
7394       int next_jx = player->jx + (player->jx - player->last_jx);
7395       int next_jy = player->jy + (player->jy - player->last_jy);
7396
7397       if (x == next_jx && y == next_jy)
7398         return TRUE;
7399     }
7400   }
7401
7402   return FALSE;
7403 }
7404
7405 static void StartMoving(int x, int y)
7406 {
7407   boolean started_moving = FALSE;       // some elements can fall _and_ move
7408   int element = Feld[x][y];
7409
7410   if (Stop[x][y])
7411     return;
7412
7413   if (MovDelay[x][y] == 0)
7414     GfxAction[x][y] = ACTION_DEFAULT;
7415
7416   if (CAN_FALL(element) && y < lev_fieldy - 1)
7417   {
7418     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7419         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7420       if (JustBeingPushed(x, y))
7421         return;
7422
7423     if (element == EL_QUICKSAND_FULL)
7424     {
7425       if (IS_FREE(x, y + 1))
7426       {
7427         InitMovingField(x, y, MV_DOWN);
7428         started_moving = TRUE;
7429
7430         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7431 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7432         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7433           Store[x][y] = EL_ROCK;
7434 #else
7435         Store[x][y] = EL_ROCK;
7436 #endif
7437
7438         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7439       }
7440       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7441       {
7442         if (!MovDelay[x][y])
7443         {
7444           MovDelay[x][y] = TILEY + 1;
7445
7446           ResetGfxAnimation(x, y);
7447           ResetGfxAnimation(x, y + 1);
7448         }
7449
7450         if (MovDelay[x][y])
7451         {
7452           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7453           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7454
7455           MovDelay[x][y]--;
7456           if (MovDelay[x][y])
7457             return;
7458         }
7459
7460         Feld[x][y] = EL_QUICKSAND_EMPTY;
7461         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7462         Store[x][y + 1] = Store[x][y];
7463         Store[x][y] = 0;
7464
7465         PlayLevelSoundAction(x, y, ACTION_FILLING);
7466       }
7467       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7468       {
7469         if (!MovDelay[x][y])
7470         {
7471           MovDelay[x][y] = TILEY + 1;
7472
7473           ResetGfxAnimation(x, y);
7474           ResetGfxAnimation(x, y + 1);
7475         }
7476
7477         if (MovDelay[x][y])
7478         {
7479           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7480           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7481
7482           MovDelay[x][y]--;
7483           if (MovDelay[x][y])
7484             return;
7485         }
7486
7487         Feld[x][y] = EL_QUICKSAND_EMPTY;
7488         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7489         Store[x][y + 1] = Store[x][y];
7490         Store[x][y] = 0;
7491
7492         PlayLevelSoundAction(x, y, ACTION_FILLING);
7493       }
7494     }
7495     else if (element == EL_QUICKSAND_FAST_FULL)
7496     {
7497       if (IS_FREE(x, y + 1))
7498       {
7499         InitMovingField(x, y, MV_DOWN);
7500         started_moving = TRUE;
7501
7502         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7503 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7504         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7505           Store[x][y] = EL_ROCK;
7506 #else
7507         Store[x][y] = EL_ROCK;
7508 #endif
7509
7510         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7511       }
7512       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7513       {
7514         if (!MovDelay[x][y])
7515         {
7516           MovDelay[x][y] = TILEY + 1;
7517
7518           ResetGfxAnimation(x, y);
7519           ResetGfxAnimation(x, y + 1);
7520         }
7521
7522         if (MovDelay[x][y])
7523         {
7524           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7525           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7526
7527           MovDelay[x][y]--;
7528           if (MovDelay[x][y])
7529             return;
7530         }
7531
7532         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7533         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7534         Store[x][y + 1] = Store[x][y];
7535         Store[x][y] = 0;
7536
7537         PlayLevelSoundAction(x, y, ACTION_FILLING);
7538       }
7539       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7540       {
7541         if (!MovDelay[x][y])
7542         {
7543           MovDelay[x][y] = TILEY + 1;
7544
7545           ResetGfxAnimation(x, y);
7546           ResetGfxAnimation(x, y + 1);
7547         }
7548
7549         if (MovDelay[x][y])
7550         {
7551           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7552           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7553
7554           MovDelay[x][y]--;
7555           if (MovDelay[x][y])
7556             return;
7557         }
7558
7559         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7560         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7561         Store[x][y + 1] = Store[x][y];
7562         Store[x][y] = 0;
7563
7564         PlayLevelSoundAction(x, y, ACTION_FILLING);
7565       }
7566     }
7567     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7568              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7569     {
7570       InitMovingField(x, y, MV_DOWN);
7571       started_moving = TRUE;
7572
7573       Feld[x][y] = EL_QUICKSAND_FILLING;
7574       Store[x][y] = element;
7575
7576       PlayLevelSoundAction(x, y, ACTION_FILLING);
7577     }
7578     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7579              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7580     {
7581       InitMovingField(x, y, MV_DOWN);
7582       started_moving = TRUE;
7583
7584       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7585       Store[x][y] = element;
7586
7587       PlayLevelSoundAction(x, y, ACTION_FILLING);
7588     }
7589     else if (element == EL_MAGIC_WALL_FULL)
7590     {
7591       if (IS_FREE(x, y + 1))
7592       {
7593         InitMovingField(x, y, MV_DOWN);
7594         started_moving = TRUE;
7595
7596         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7597         Store[x][y] = EL_CHANGED(Store[x][y]);
7598       }
7599       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7600       {
7601         if (!MovDelay[x][y])
7602           MovDelay[x][y] = TILEY / 4 + 1;
7603
7604         if (MovDelay[x][y])
7605         {
7606           MovDelay[x][y]--;
7607           if (MovDelay[x][y])
7608             return;
7609         }
7610
7611         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7612         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7613         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7614         Store[x][y] = 0;
7615       }
7616     }
7617     else if (element == EL_BD_MAGIC_WALL_FULL)
7618     {
7619       if (IS_FREE(x, y + 1))
7620       {
7621         InitMovingField(x, y, MV_DOWN);
7622         started_moving = TRUE;
7623
7624         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7625         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7626       }
7627       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7628       {
7629         if (!MovDelay[x][y])
7630           MovDelay[x][y] = TILEY / 4 + 1;
7631
7632         if (MovDelay[x][y])
7633         {
7634           MovDelay[x][y]--;
7635           if (MovDelay[x][y])
7636             return;
7637         }
7638
7639         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7640         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7641         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7642         Store[x][y] = 0;
7643       }
7644     }
7645     else if (element == EL_DC_MAGIC_WALL_FULL)
7646     {
7647       if (IS_FREE(x, y + 1))
7648       {
7649         InitMovingField(x, y, MV_DOWN);
7650         started_moving = TRUE;
7651
7652         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7653         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7654       }
7655       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7656       {
7657         if (!MovDelay[x][y])
7658           MovDelay[x][y] = TILEY / 4 + 1;
7659
7660         if (MovDelay[x][y])
7661         {
7662           MovDelay[x][y]--;
7663           if (MovDelay[x][y])
7664             return;
7665         }
7666
7667         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7668         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7669         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7670         Store[x][y] = 0;
7671       }
7672     }
7673     else if ((CAN_PASS_MAGIC_WALL(element) &&
7674               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7675                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7676              (CAN_PASS_DC_MAGIC_WALL(element) &&
7677               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7678
7679     {
7680       InitMovingField(x, y, MV_DOWN);
7681       started_moving = TRUE;
7682
7683       Feld[x][y] =
7684         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7685          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7686          EL_DC_MAGIC_WALL_FILLING);
7687       Store[x][y] = element;
7688     }
7689     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7690     {
7691       SplashAcid(x, y + 1);
7692
7693       InitMovingField(x, y, MV_DOWN);
7694       started_moving = TRUE;
7695
7696       Store[x][y] = EL_ACID;
7697     }
7698     else if (
7699              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7700               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7701              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7702               CAN_FALL(element) && WasJustFalling[x][y] &&
7703               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7704
7705              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7706               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7707               (Feld[x][y + 1] == EL_BLOCKED)))
7708     {
7709       /* this is needed for a special case not covered by calling "Impact()"
7710          from "ContinueMoving()": if an element moves to a tile directly below
7711          another element which was just falling on that tile (which was empty
7712          in the previous frame), the falling element above would just stop
7713          instead of smashing the element below (in previous version, the above
7714          element was just checked for "moving" instead of "falling", resulting
7715          in incorrect smashes caused by horizontal movement of the above
7716          element; also, the case of the player being the element to smash was
7717          simply not covered here... :-/ ) */
7718
7719       CheckCollision[x][y] = 0;
7720       CheckImpact[x][y] = 0;
7721
7722       Impact(x, y);
7723     }
7724     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7725     {
7726       if (MovDir[x][y] == MV_NONE)
7727       {
7728         InitMovingField(x, y, MV_DOWN);
7729         started_moving = TRUE;
7730       }
7731     }
7732     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7733     {
7734       if (WasJustFalling[x][y]) // prevent animation from being restarted
7735         MovDir[x][y] = MV_DOWN;
7736
7737       InitMovingField(x, y, MV_DOWN);
7738       started_moving = TRUE;
7739     }
7740     else if (element == EL_AMOEBA_DROP)
7741     {
7742       Feld[x][y] = EL_AMOEBA_GROWING;
7743       Store[x][y] = EL_AMOEBA_WET;
7744     }
7745     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7746               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7747              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7748              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7749     {
7750       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7751                                 (IS_FREE(x - 1, y + 1) ||
7752                                  Feld[x - 1][y + 1] == EL_ACID));
7753       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7754                                 (IS_FREE(x + 1, y + 1) ||
7755                                  Feld[x + 1][y + 1] == EL_ACID));
7756       boolean can_fall_any  = (can_fall_left || can_fall_right);
7757       boolean can_fall_both = (can_fall_left && can_fall_right);
7758       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7759
7760       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7761       {
7762         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7763           can_fall_right = FALSE;
7764         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7765           can_fall_left = FALSE;
7766         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7767           can_fall_right = FALSE;
7768         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7769           can_fall_left = FALSE;
7770
7771         can_fall_any  = (can_fall_left || can_fall_right);
7772         can_fall_both = FALSE;
7773       }
7774
7775       if (can_fall_both)
7776       {
7777         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7778           can_fall_right = FALSE;       // slip down on left side
7779         else
7780           can_fall_left = !(can_fall_right = RND(2));
7781
7782         can_fall_both = FALSE;
7783       }
7784
7785       if (can_fall_any)
7786       {
7787         // if not determined otherwise, prefer left side for slipping down
7788         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7789         started_moving = TRUE;
7790       }
7791     }
7792     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7793     {
7794       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7795       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7796       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7797       int belt_dir = game.belt_dir[belt_nr];
7798
7799       if ((belt_dir == MV_LEFT  && left_is_free) ||
7800           (belt_dir == MV_RIGHT && right_is_free))
7801       {
7802         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7803
7804         InitMovingField(x, y, belt_dir);
7805         started_moving = TRUE;
7806
7807         Pushed[x][y] = TRUE;
7808         Pushed[nextx][y] = TRUE;
7809
7810         GfxAction[x][y] = ACTION_DEFAULT;
7811       }
7812       else
7813       {
7814         MovDir[x][y] = 0;       // if element was moving, stop it
7815       }
7816     }
7817   }
7818
7819   // not "else if" because of elements that can fall and move (EL_SPRING)
7820   if (CAN_MOVE(element) && !started_moving)
7821   {
7822     int move_pattern = element_info[element].move_pattern;
7823     int newx, newy;
7824
7825     Moving2Blocked(x, y, &newx, &newy);
7826
7827     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7828       return;
7829
7830     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7831         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7832     {
7833       WasJustMoving[x][y] = 0;
7834       CheckCollision[x][y] = 0;
7835
7836       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7837
7838       if (Feld[x][y] != element)        // element has changed
7839         return;
7840     }
7841
7842     if (!MovDelay[x][y])        // start new movement phase
7843     {
7844       // all objects that can change their move direction after each step
7845       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
7846
7847       if (element != EL_YAMYAM &&
7848           element != EL_DARK_YAMYAM &&
7849           element != EL_PACMAN &&
7850           !(move_pattern & MV_ANY_DIRECTION) &&
7851           move_pattern != MV_TURNING_LEFT &&
7852           move_pattern != MV_TURNING_RIGHT &&
7853           move_pattern != MV_TURNING_LEFT_RIGHT &&
7854           move_pattern != MV_TURNING_RIGHT_LEFT &&
7855           move_pattern != MV_TURNING_RANDOM)
7856       {
7857         TurnRound(x, y);
7858
7859         if (MovDelay[x][y] && (element == EL_BUG ||
7860                                element == EL_SPACESHIP ||
7861                                element == EL_SP_SNIKSNAK ||
7862                                element == EL_SP_ELECTRON ||
7863                                element == EL_MOLE))
7864           TEST_DrawLevelField(x, y);
7865       }
7866     }
7867
7868     if (MovDelay[x][y])         // wait some time before next movement
7869     {
7870       MovDelay[x][y]--;
7871
7872       if (element == EL_ROBOT ||
7873           element == EL_YAMYAM ||
7874           element == EL_DARK_YAMYAM)
7875       {
7876         DrawLevelElementAnimationIfNeeded(x, y, element);
7877         PlayLevelSoundAction(x, y, ACTION_WAITING);
7878       }
7879       else if (element == EL_SP_ELECTRON)
7880         DrawLevelElementAnimationIfNeeded(x, y, element);
7881       else if (element == EL_DRAGON)
7882       {
7883         int i;
7884         int dir = MovDir[x][y];
7885         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7886         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7887         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7888                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7889                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7890                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7891         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7892
7893         GfxAction[x][y] = ACTION_ATTACKING;
7894
7895         if (IS_PLAYER(x, y))
7896           DrawPlayerField(x, y);
7897         else
7898           TEST_DrawLevelField(x, y);
7899
7900         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7901
7902         for (i = 1; i <= 3; i++)
7903         {
7904           int xx = x + i * dx;
7905           int yy = y + i * dy;
7906           int sx = SCREENX(xx);
7907           int sy = SCREENY(yy);
7908           int flame_graphic = graphic + (i - 1);
7909
7910           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7911             break;
7912
7913           if (MovDelay[x][y])
7914           {
7915             int flamed = MovingOrBlocked2Element(xx, yy);
7916
7917             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7918               Bang(xx, yy);
7919             else
7920               RemoveMovingField(xx, yy);
7921
7922             ChangeDelay[xx][yy] = 0;
7923
7924             Feld[xx][yy] = EL_FLAMES;
7925
7926             if (IN_SCR_FIELD(sx, sy))
7927             {
7928               TEST_DrawLevelFieldCrumbled(xx, yy);
7929               DrawGraphic(sx, sy, flame_graphic, frame);
7930             }
7931           }
7932           else
7933           {
7934             if (Feld[xx][yy] == EL_FLAMES)
7935               Feld[xx][yy] = EL_EMPTY;
7936             TEST_DrawLevelField(xx, yy);
7937           }
7938         }
7939       }
7940
7941       if (MovDelay[x][y])       // element still has to wait some time
7942       {
7943         PlayLevelSoundAction(x, y, ACTION_WAITING);
7944
7945         return;
7946       }
7947     }
7948
7949     // now make next step
7950
7951     Moving2Blocked(x, y, &newx, &newy); // get next screen position
7952
7953     if (DONT_COLLIDE_WITH(element) &&
7954         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7955         !PLAYER_ENEMY_PROTECTED(newx, newy))
7956     {
7957       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7958
7959       return;
7960     }
7961
7962     else if (CAN_MOVE_INTO_ACID(element) &&
7963              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7964              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7965              (MovDir[x][y] == MV_DOWN ||
7966               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7967     {
7968       SplashAcid(newx, newy);
7969       Store[x][y] = EL_ACID;
7970     }
7971     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7972     {
7973       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7974           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7975           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7976           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7977       {
7978         RemoveField(x, y);
7979         TEST_DrawLevelField(x, y);
7980
7981         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7982         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7983           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7984
7985         game.friends_still_needed--;
7986         if (!game.friends_still_needed &&
7987             !game.GameOver &&
7988             game.all_players_gone)
7989           LevelSolved();
7990
7991         return;
7992       }
7993       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7994       {
7995         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7996           TEST_DrawLevelField(newx, newy);
7997         else
7998           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7999       }
8000       else if (!IS_FREE(newx, newy))
8001       {
8002         GfxAction[x][y] = ACTION_WAITING;
8003
8004         if (IS_PLAYER(x, y))
8005           DrawPlayerField(x, y);
8006         else
8007           TEST_DrawLevelField(x, y);
8008
8009         return;
8010       }
8011     }
8012     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8013     {
8014       if (IS_FOOD_PIG(Feld[newx][newy]))
8015       {
8016         if (IS_MOVING(newx, newy))
8017           RemoveMovingField(newx, newy);
8018         else
8019         {
8020           Feld[newx][newy] = EL_EMPTY;
8021           TEST_DrawLevelField(newx, newy);
8022         }
8023
8024         PlayLevelSound(x, y, SND_PIG_DIGGING);
8025       }
8026       else if (!IS_FREE(newx, newy))
8027       {
8028         if (IS_PLAYER(x, y))
8029           DrawPlayerField(x, y);
8030         else
8031           TEST_DrawLevelField(x, y);
8032
8033         return;
8034       }
8035     }
8036     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8037     {
8038       if (Store[x][y] != EL_EMPTY)
8039       {
8040         boolean can_clone = FALSE;
8041         int xx, yy;
8042
8043         // check if element to clone is still there
8044         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8045         {
8046           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8047           {
8048             can_clone = TRUE;
8049
8050             break;
8051           }
8052         }
8053
8054         // cannot clone or target field not free anymore -- do not clone
8055         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8056           Store[x][y] = EL_EMPTY;
8057       }
8058
8059       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8060       {
8061         if (IS_MV_DIAGONAL(MovDir[x][y]))
8062         {
8063           int diagonal_move_dir = MovDir[x][y];
8064           int stored = Store[x][y];
8065           int change_delay = 8;
8066           int graphic;
8067
8068           // android is moving diagonally
8069
8070           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8071
8072           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8073           GfxElement[x][y] = EL_EMC_ANDROID;
8074           GfxAction[x][y] = ACTION_SHRINKING;
8075           GfxDir[x][y] = diagonal_move_dir;
8076           ChangeDelay[x][y] = change_delay;
8077
8078           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8079                                    GfxDir[x][y]);
8080
8081           DrawLevelGraphicAnimation(x, y, graphic);
8082           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8083
8084           if (Feld[newx][newy] == EL_ACID)
8085           {
8086             SplashAcid(newx, newy);
8087
8088             return;
8089           }
8090
8091           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8092
8093           Store[newx][newy] = EL_EMC_ANDROID;
8094           GfxElement[newx][newy] = EL_EMC_ANDROID;
8095           GfxAction[newx][newy] = ACTION_GROWING;
8096           GfxDir[newx][newy] = diagonal_move_dir;
8097           ChangeDelay[newx][newy] = change_delay;
8098
8099           graphic = el_act_dir2img(GfxElement[newx][newy],
8100                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8101
8102           DrawLevelGraphicAnimation(newx, newy, graphic);
8103           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8104
8105           return;
8106         }
8107         else
8108         {
8109           Feld[newx][newy] = EL_EMPTY;
8110           TEST_DrawLevelField(newx, newy);
8111
8112           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8113         }
8114       }
8115       else if (!IS_FREE(newx, newy))
8116       {
8117         return;
8118       }
8119     }
8120     else if (IS_CUSTOM_ELEMENT(element) &&
8121              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8122     {
8123       if (!DigFieldByCE(newx, newy, element))
8124         return;
8125
8126       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8127       {
8128         RunnerVisit[x][y] = FrameCounter;
8129         PlayerVisit[x][y] /= 8;         // expire player visit path
8130       }
8131     }
8132     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8133     {
8134       if (!IS_FREE(newx, newy))
8135       {
8136         if (IS_PLAYER(x, y))
8137           DrawPlayerField(x, y);
8138         else
8139           TEST_DrawLevelField(x, y);
8140
8141         return;
8142       }
8143       else
8144       {
8145         boolean wanna_flame = !RND(10);
8146         int dx = newx - x, dy = newy - y;
8147         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8148         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8149         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8150                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8151         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8152                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8153
8154         if ((wanna_flame ||
8155              IS_CLASSIC_ENEMY(element1) ||
8156              IS_CLASSIC_ENEMY(element2)) &&
8157             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8158             element1 != EL_FLAMES && element2 != EL_FLAMES)
8159         {
8160           ResetGfxAnimation(x, y);
8161           GfxAction[x][y] = ACTION_ATTACKING;
8162
8163           if (IS_PLAYER(x, y))
8164             DrawPlayerField(x, y);
8165           else
8166             TEST_DrawLevelField(x, y);
8167
8168           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8169
8170           MovDelay[x][y] = 50;
8171
8172           Feld[newx][newy] = EL_FLAMES;
8173           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8174             Feld[newx1][newy1] = EL_FLAMES;
8175           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8176             Feld[newx2][newy2] = EL_FLAMES;
8177
8178           return;
8179         }
8180       }
8181     }
8182     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8183              Feld[newx][newy] == EL_DIAMOND)
8184     {
8185       if (IS_MOVING(newx, newy))
8186         RemoveMovingField(newx, newy);
8187       else
8188       {
8189         Feld[newx][newy] = EL_EMPTY;
8190         TEST_DrawLevelField(newx, newy);
8191       }
8192
8193       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8194     }
8195     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8196              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8197     {
8198       if (AmoebaNr[newx][newy])
8199       {
8200         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8201         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8202             Feld[newx][newy] == EL_BD_AMOEBA)
8203           AmoebaCnt[AmoebaNr[newx][newy]]--;
8204       }
8205
8206       if (IS_MOVING(newx, newy))
8207       {
8208         RemoveMovingField(newx, newy);
8209       }
8210       else
8211       {
8212         Feld[newx][newy] = EL_EMPTY;
8213         TEST_DrawLevelField(newx, newy);
8214       }
8215
8216       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8217     }
8218     else if ((element == EL_PACMAN || element == EL_MOLE)
8219              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8220     {
8221       if (AmoebaNr[newx][newy])
8222       {
8223         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8224         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8225             Feld[newx][newy] == EL_BD_AMOEBA)
8226           AmoebaCnt[AmoebaNr[newx][newy]]--;
8227       }
8228
8229       if (element == EL_MOLE)
8230       {
8231         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8232         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8233
8234         ResetGfxAnimation(x, y);
8235         GfxAction[x][y] = ACTION_DIGGING;
8236         TEST_DrawLevelField(x, y);
8237
8238         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8239
8240         return;                         // wait for shrinking amoeba
8241       }
8242       else      // element == EL_PACMAN
8243       {
8244         Feld[newx][newy] = EL_EMPTY;
8245         TEST_DrawLevelField(newx, newy);
8246         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8247       }
8248     }
8249     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8250              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8251               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8252     {
8253       // wait for shrinking amoeba to completely disappear
8254       return;
8255     }
8256     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8257     {
8258       // object was running against a wall
8259
8260       TurnRound(x, y);
8261
8262       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8263         DrawLevelElementAnimation(x, y, element);
8264
8265       if (DONT_TOUCH(element))
8266         TestIfBadThingTouchesPlayer(x, y);
8267
8268       return;
8269     }
8270
8271     InitMovingField(x, y, MovDir[x][y]);
8272
8273     PlayLevelSoundAction(x, y, ACTION_MOVING);
8274   }
8275
8276   if (MovDir[x][y])
8277     ContinueMoving(x, y);
8278 }
8279
8280 void ContinueMoving(int x, int y)
8281 {
8282   int element = Feld[x][y];
8283   struct ElementInfo *ei = &element_info[element];
8284   int direction = MovDir[x][y];
8285   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8286   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8287   int newx = x + dx, newy = y + dy;
8288   int stored = Store[x][y];
8289   int stored_new = Store[newx][newy];
8290   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8291   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8292   boolean last_line = (newy == lev_fieldy - 1);
8293
8294   MovPos[x][y] += getElementMoveStepsize(x, y);
8295
8296   if (pushed_by_player) // special case: moving object pushed by player
8297     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8298
8299   if (ABS(MovPos[x][y]) < TILEX)
8300   {
8301     TEST_DrawLevelField(x, y);
8302
8303     return;     // element is still moving
8304   }
8305
8306   // element reached destination field
8307
8308   Feld[x][y] = EL_EMPTY;
8309   Feld[newx][newy] = element;
8310   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8311
8312   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8313   {
8314     element = Feld[newx][newy] = EL_ACID;
8315   }
8316   else if (element == EL_MOLE)
8317   {
8318     Feld[x][y] = EL_SAND;
8319
8320     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8321   }
8322   else if (element == EL_QUICKSAND_FILLING)
8323   {
8324     element = Feld[newx][newy] = get_next_element(element);
8325     Store[newx][newy] = Store[x][y];
8326   }
8327   else if (element == EL_QUICKSAND_EMPTYING)
8328   {
8329     Feld[x][y] = get_next_element(element);
8330     element = Feld[newx][newy] = Store[x][y];
8331   }
8332   else if (element == EL_QUICKSAND_FAST_FILLING)
8333   {
8334     element = Feld[newx][newy] = get_next_element(element);
8335     Store[newx][newy] = Store[x][y];
8336   }
8337   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8338   {
8339     Feld[x][y] = get_next_element(element);
8340     element = Feld[newx][newy] = Store[x][y];
8341   }
8342   else if (element == EL_MAGIC_WALL_FILLING)
8343   {
8344     element = Feld[newx][newy] = get_next_element(element);
8345     if (!game.magic_wall_active)
8346       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8347     Store[newx][newy] = Store[x][y];
8348   }
8349   else if (element == EL_MAGIC_WALL_EMPTYING)
8350   {
8351     Feld[x][y] = get_next_element(element);
8352     if (!game.magic_wall_active)
8353       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8354     element = Feld[newx][newy] = Store[x][y];
8355
8356     InitField(newx, newy, FALSE);
8357   }
8358   else if (element == EL_BD_MAGIC_WALL_FILLING)
8359   {
8360     element = Feld[newx][newy] = get_next_element(element);
8361     if (!game.magic_wall_active)
8362       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8363     Store[newx][newy] = Store[x][y];
8364   }
8365   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8366   {
8367     Feld[x][y] = get_next_element(element);
8368     if (!game.magic_wall_active)
8369       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8370     element = Feld[newx][newy] = Store[x][y];
8371
8372     InitField(newx, newy, FALSE);
8373   }
8374   else if (element == EL_DC_MAGIC_WALL_FILLING)
8375   {
8376     element = Feld[newx][newy] = get_next_element(element);
8377     if (!game.magic_wall_active)
8378       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8379     Store[newx][newy] = Store[x][y];
8380   }
8381   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8382   {
8383     Feld[x][y] = get_next_element(element);
8384     if (!game.magic_wall_active)
8385       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8386     element = Feld[newx][newy] = Store[x][y];
8387
8388     InitField(newx, newy, FALSE);
8389   }
8390   else if (element == EL_AMOEBA_DROPPING)
8391   {
8392     Feld[x][y] = get_next_element(element);
8393     element = Feld[newx][newy] = Store[x][y];
8394   }
8395   else if (element == EL_SOKOBAN_OBJECT)
8396   {
8397     if (Back[x][y])
8398       Feld[x][y] = Back[x][y];
8399
8400     if (Back[newx][newy])
8401       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8402
8403     Back[x][y] = Back[newx][newy] = 0;
8404   }
8405
8406   Store[x][y] = EL_EMPTY;
8407   MovPos[x][y] = 0;
8408   MovDir[x][y] = 0;
8409   MovDelay[x][y] = 0;
8410
8411   MovDelay[newx][newy] = 0;
8412
8413   if (CAN_CHANGE_OR_HAS_ACTION(element))
8414   {
8415     // copy element change control values to new field
8416     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8417     ChangePage[newx][newy]  = ChangePage[x][y];
8418     ChangeCount[newx][newy] = ChangeCount[x][y];
8419     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8420   }
8421
8422   CustomValue[newx][newy] = CustomValue[x][y];
8423
8424   ChangeDelay[x][y] = 0;
8425   ChangePage[x][y] = -1;
8426   ChangeCount[x][y] = 0;
8427   ChangeEvent[x][y] = -1;
8428
8429   CustomValue[x][y] = 0;
8430
8431   // copy animation control values to new field
8432   GfxFrame[newx][newy]  = GfxFrame[x][y];
8433   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8434   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8435   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8436
8437   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8438
8439   // some elements can leave other elements behind after moving
8440   if (ei->move_leave_element != EL_EMPTY &&
8441       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8442       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8443   {
8444     int move_leave_element = ei->move_leave_element;
8445
8446     // this makes it possible to leave the removed element again
8447     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8448       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8449
8450     Feld[x][y] = move_leave_element;
8451
8452     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8453       MovDir[x][y] = direction;
8454
8455     InitField(x, y, FALSE);
8456
8457     if (GFX_CRUMBLED(Feld[x][y]))
8458       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8459
8460     if (ELEM_IS_PLAYER(move_leave_element))
8461       RelocatePlayer(x, y, move_leave_element);
8462   }
8463
8464   // do this after checking for left-behind element
8465   ResetGfxAnimation(x, y);      // reset animation values for old field
8466
8467   if (!CAN_MOVE(element) ||
8468       (CAN_FALL(element) && direction == MV_DOWN &&
8469        (element == EL_SPRING ||
8470         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8471         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8472     GfxDir[x][y] = MovDir[newx][newy] = 0;
8473
8474   TEST_DrawLevelField(x, y);
8475   TEST_DrawLevelField(newx, newy);
8476
8477   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8478
8479   // prevent pushed element from moving on in pushed direction
8480   if (pushed_by_player && CAN_MOVE(element) &&
8481       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8482       !(element_info[element].move_pattern & direction))
8483     TurnRound(newx, newy);
8484
8485   // prevent elements on conveyor belt from moving on in last direction
8486   if (pushed_by_conveyor && CAN_FALL(element) &&
8487       direction & MV_HORIZONTAL)
8488     MovDir[newx][newy] = 0;
8489
8490   if (!pushed_by_player)
8491   {
8492     int nextx = newx + dx, nexty = newy + dy;
8493     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8494
8495     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8496
8497     if (CAN_FALL(element) && direction == MV_DOWN)
8498       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8499
8500     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8501       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8502
8503     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8504       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8505   }
8506
8507   if (DONT_TOUCH(element))      // object may be nasty to player or others
8508   {
8509     TestIfBadThingTouchesPlayer(newx, newy);
8510     TestIfBadThingTouchesFriend(newx, newy);
8511
8512     if (!IS_CUSTOM_ELEMENT(element))
8513       TestIfBadThingTouchesOtherBadThing(newx, newy);
8514   }
8515   else if (element == EL_PENGUIN)
8516     TestIfFriendTouchesBadThing(newx, newy);
8517
8518   if (DONT_GET_HIT_BY(element))
8519   {
8520     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8521   }
8522
8523   // give the player one last chance (one more frame) to move away
8524   if (CAN_FALL(element) && direction == MV_DOWN &&
8525       (last_line || (!IS_FREE(x, newy + 1) &&
8526                      (!IS_PLAYER(x, newy + 1) ||
8527                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8528     Impact(x, newy);
8529
8530   if (pushed_by_player && !game.use_change_when_pushing_bug)
8531   {
8532     int push_side = MV_DIR_OPPOSITE(direction);
8533     struct PlayerInfo *player = PLAYERINFO(x, y);
8534
8535     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8536                                player->index_bit, push_side);
8537     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8538                                         player->index_bit, push_side);
8539   }
8540
8541   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8542     MovDelay[newx][newy] = 1;
8543
8544   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8545
8546   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8547   TestIfElementHitsCustomElement(newx, newy, direction);
8548   TestIfPlayerTouchesCustomElement(newx, newy);
8549   TestIfElementTouchesCustomElement(newx, newy);
8550
8551   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8552       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8553     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8554                              MV_DIR_OPPOSITE(direction));
8555 }
8556
8557 int AmoebeNachbarNr(int ax, int ay)
8558 {
8559   int i;
8560   int element = Feld[ax][ay];
8561   int group_nr = 0;
8562   static int xy[4][2] =
8563   {
8564     { 0, -1 },
8565     { -1, 0 },
8566     { +1, 0 },
8567     { 0, +1 }
8568   };
8569
8570   for (i = 0; i < NUM_DIRECTIONS; i++)
8571   {
8572     int x = ax + xy[i][0];
8573     int y = ay + xy[i][1];
8574
8575     if (!IN_LEV_FIELD(x, y))
8576       continue;
8577
8578     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8579       group_nr = AmoebaNr[x][y];
8580   }
8581
8582   return group_nr;
8583 }
8584
8585 static void AmoebenVereinigen(int ax, int ay)
8586 {
8587   int i, x, y, xx, yy;
8588   int new_group_nr = AmoebaNr[ax][ay];
8589   static int xy[4][2] =
8590   {
8591     { 0, -1 },
8592     { -1, 0 },
8593     { +1, 0 },
8594     { 0, +1 }
8595   };
8596
8597   if (new_group_nr == 0)
8598     return;
8599
8600   for (i = 0; i < NUM_DIRECTIONS; i++)
8601   {
8602     x = ax + xy[i][0];
8603     y = ay + xy[i][1];
8604
8605     if (!IN_LEV_FIELD(x, y))
8606       continue;
8607
8608     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8609          Feld[x][y] == EL_BD_AMOEBA ||
8610          Feld[x][y] == EL_AMOEBA_DEAD) &&
8611         AmoebaNr[x][y] != new_group_nr)
8612     {
8613       int old_group_nr = AmoebaNr[x][y];
8614
8615       if (old_group_nr == 0)
8616         return;
8617
8618       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8619       AmoebaCnt[old_group_nr] = 0;
8620       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8621       AmoebaCnt2[old_group_nr] = 0;
8622
8623       SCAN_PLAYFIELD(xx, yy)
8624       {
8625         if (AmoebaNr[xx][yy] == old_group_nr)
8626           AmoebaNr[xx][yy] = new_group_nr;
8627       }
8628     }
8629   }
8630 }
8631
8632 void AmoebeUmwandeln(int ax, int ay)
8633 {
8634   int i, x, y;
8635
8636   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8637   {
8638     int group_nr = AmoebaNr[ax][ay];
8639
8640 #ifdef DEBUG
8641     if (group_nr == 0)
8642     {
8643       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8644       printf("AmoebeUmwandeln(): This should never happen!\n");
8645       return;
8646     }
8647 #endif
8648
8649     SCAN_PLAYFIELD(x, y)
8650     {
8651       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8652       {
8653         AmoebaNr[x][y] = 0;
8654         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8655       }
8656     }
8657
8658     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8659                             SND_AMOEBA_TURNING_TO_GEM :
8660                             SND_AMOEBA_TURNING_TO_ROCK));
8661     Bang(ax, ay);
8662   }
8663   else
8664   {
8665     static int xy[4][2] =
8666     {
8667       { 0, -1 },
8668       { -1, 0 },
8669       { +1, 0 },
8670       { 0, +1 }
8671     };
8672
8673     for (i = 0; i < NUM_DIRECTIONS; i++)
8674     {
8675       x = ax + xy[i][0];
8676       y = ay + xy[i][1];
8677
8678       if (!IN_LEV_FIELD(x, y))
8679         continue;
8680
8681       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8682       {
8683         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8684                               SND_AMOEBA_TURNING_TO_GEM :
8685                               SND_AMOEBA_TURNING_TO_ROCK));
8686         Bang(x, y);
8687       }
8688     }
8689   }
8690 }
8691
8692 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8693 {
8694   int x, y;
8695   int group_nr = AmoebaNr[ax][ay];
8696   boolean done = FALSE;
8697
8698 #ifdef DEBUG
8699   if (group_nr == 0)
8700   {
8701     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8702     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8703     return;
8704   }
8705 #endif
8706
8707   SCAN_PLAYFIELD(x, y)
8708   {
8709     if (AmoebaNr[x][y] == group_nr &&
8710         (Feld[x][y] == EL_AMOEBA_DEAD ||
8711          Feld[x][y] == EL_BD_AMOEBA ||
8712          Feld[x][y] == EL_AMOEBA_GROWING))
8713     {
8714       AmoebaNr[x][y] = 0;
8715       Feld[x][y] = new_element;
8716       InitField(x, y, FALSE);
8717       TEST_DrawLevelField(x, y);
8718       done = TRUE;
8719     }
8720   }
8721
8722   if (done)
8723     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8724                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8725                             SND_BD_AMOEBA_TURNING_TO_GEM));
8726 }
8727
8728 static void AmoebeWaechst(int x, int y)
8729 {
8730   static unsigned int sound_delay = 0;
8731   static unsigned int sound_delay_value = 0;
8732
8733   if (!MovDelay[x][y])          // start new growing cycle
8734   {
8735     MovDelay[x][y] = 7;
8736
8737     if (DelayReached(&sound_delay, sound_delay_value))
8738     {
8739       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8740       sound_delay_value = 30;
8741     }
8742   }
8743
8744   if (MovDelay[x][y])           // wait some time before growing bigger
8745   {
8746     MovDelay[x][y]--;
8747     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8748     {
8749       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8750                                            6 - MovDelay[x][y]);
8751
8752       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8753     }
8754
8755     if (!MovDelay[x][y])
8756     {
8757       Feld[x][y] = Store[x][y];
8758       Store[x][y] = 0;
8759       TEST_DrawLevelField(x, y);
8760     }
8761   }
8762 }
8763
8764 static void AmoebaDisappearing(int x, int y)
8765 {
8766   static unsigned int sound_delay = 0;
8767   static unsigned int sound_delay_value = 0;
8768
8769   if (!MovDelay[x][y])          // start new shrinking cycle
8770   {
8771     MovDelay[x][y] = 7;
8772
8773     if (DelayReached(&sound_delay, sound_delay_value))
8774       sound_delay_value = 30;
8775   }
8776
8777   if (MovDelay[x][y])           // wait some time before shrinking
8778   {
8779     MovDelay[x][y]--;
8780     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8781     {
8782       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8783                                            6 - MovDelay[x][y]);
8784
8785       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8786     }
8787
8788     if (!MovDelay[x][y])
8789     {
8790       Feld[x][y] = EL_EMPTY;
8791       TEST_DrawLevelField(x, y);
8792
8793       // don't let mole enter this field in this cycle;
8794       // (give priority to objects falling to this field from above)
8795       Stop[x][y] = TRUE;
8796     }
8797   }
8798 }
8799
8800 static void AmoebeAbleger(int ax, int ay)
8801 {
8802   int i;
8803   int element = Feld[ax][ay];
8804   int graphic = el2img(element);
8805   int newax = ax, neway = ay;
8806   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8807   static int xy[4][2] =
8808   {
8809     { 0, -1 },
8810     { -1, 0 },
8811     { +1, 0 },
8812     { 0, +1 }
8813   };
8814
8815   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8816   {
8817     Feld[ax][ay] = EL_AMOEBA_DEAD;
8818     TEST_DrawLevelField(ax, ay);
8819     return;
8820   }
8821
8822   if (IS_ANIMATED(graphic))
8823     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8824
8825   if (!MovDelay[ax][ay])        // start making new amoeba field
8826     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8827
8828   if (MovDelay[ax][ay])         // wait some time before making new amoeba
8829   {
8830     MovDelay[ax][ay]--;
8831     if (MovDelay[ax][ay])
8832       return;
8833   }
8834
8835   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
8836   {
8837     int start = RND(4);
8838     int x = ax + xy[start][0];
8839     int y = ay + xy[start][1];
8840
8841     if (!IN_LEV_FIELD(x, y))
8842       return;
8843
8844     if (IS_FREE(x, y) ||
8845         CAN_GROW_INTO(Feld[x][y]) ||
8846         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8847         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8848     {
8849       newax = x;
8850       neway = y;
8851     }
8852
8853     if (newax == ax && neway == ay)
8854       return;
8855   }
8856   else                          // normal or "filled" (BD style) amoeba
8857   {
8858     int start = RND(4);
8859     boolean waiting_for_player = FALSE;
8860
8861     for (i = 0; i < NUM_DIRECTIONS; i++)
8862     {
8863       int j = (start + i) % 4;
8864       int x = ax + xy[j][0];
8865       int y = ay + xy[j][1];
8866
8867       if (!IN_LEV_FIELD(x, y))
8868         continue;
8869
8870       if (IS_FREE(x, y) ||
8871           CAN_GROW_INTO(Feld[x][y]) ||
8872           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8873           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8874       {
8875         newax = x;
8876         neway = y;
8877         break;
8878       }
8879       else if (IS_PLAYER(x, y))
8880         waiting_for_player = TRUE;
8881     }
8882
8883     if (newax == ax && neway == ay)             // amoeba cannot grow
8884     {
8885       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8886       {
8887         Feld[ax][ay] = EL_AMOEBA_DEAD;
8888         TEST_DrawLevelField(ax, ay);
8889         AmoebaCnt[AmoebaNr[ax][ay]]--;
8890
8891         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
8892         {
8893           if (element == EL_AMOEBA_FULL)
8894             AmoebeUmwandeln(ax, ay);
8895           else if (element == EL_BD_AMOEBA)
8896             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8897         }
8898       }
8899       return;
8900     }
8901     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8902     {
8903       // amoeba gets larger by growing in some direction
8904
8905       int new_group_nr = AmoebaNr[ax][ay];
8906
8907 #ifdef DEBUG
8908   if (new_group_nr == 0)
8909   {
8910     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8911     printf("AmoebeAbleger(): This should never happen!\n");
8912     return;
8913   }
8914 #endif
8915
8916       AmoebaNr[newax][neway] = new_group_nr;
8917       AmoebaCnt[new_group_nr]++;
8918       AmoebaCnt2[new_group_nr]++;
8919
8920       // if amoeba touches other amoeba(s) after growing, unify them
8921       AmoebenVereinigen(newax, neway);
8922
8923       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8924       {
8925         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8926         return;
8927       }
8928     }
8929   }
8930
8931   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8932       (neway == lev_fieldy - 1 && newax != ax))
8933   {
8934     Feld[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
8935     Store[newax][neway] = element;
8936   }
8937   else if (neway == ay || element == EL_EMC_DRIPPER)
8938   {
8939     Feld[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
8940
8941     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8942   }
8943   else
8944   {
8945     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
8946     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8947     Store[ax][ay] = EL_AMOEBA_DROP;
8948     ContinueMoving(ax, ay);
8949     return;
8950   }
8951
8952   TEST_DrawLevelField(newax, neway);
8953 }
8954
8955 static void Life(int ax, int ay)
8956 {
8957   int x1, y1, x2, y2;
8958   int life_time = 40;
8959   int element = Feld[ax][ay];
8960   int graphic = el2img(element);
8961   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8962                          level.biomaze);
8963   boolean changed = FALSE;
8964
8965   if (IS_ANIMATED(graphic))
8966     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8967
8968   if (Stop[ax][ay])
8969     return;
8970
8971   if (!MovDelay[ax][ay])        // start new "game of life" cycle
8972     MovDelay[ax][ay] = life_time;
8973
8974   if (MovDelay[ax][ay])         // wait some time before next cycle
8975   {
8976     MovDelay[ax][ay]--;
8977     if (MovDelay[ax][ay])
8978       return;
8979   }
8980
8981   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8982   {
8983     int xx = ax+x1, yy = ay+y1;
8984     int old_element = Feld[xx][yy];
8985     int num_neighbours = 0;
8986
8987     if (!IN_LEV_FIELD(xx, yy))
8988       continue;
8989
8990     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8991     {
8992       int x = xx+x2, y = yy+y2;
8993
8994       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8995         continue;
8996
8997       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8998       boolean is_neighbour = FALSE;
8999
9000       if (level.use_life_bugs)
9001         is_neighbour =
9002           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9003            (IS_FREE(x, y)                             &&  Stop[x][y]));
9004       else
9005         is_neighbour =
9006           (Last[x][y] == element || is_player_cell);
9007
9008       if (is_neighbour)
9009         num_neighbours++;
9010     }
9011
9012     boolean is_free = FALSE;
9013
9014     if (level.use_life_bugs)
9015       is_free = (IS_FREE(xx, yy));
9016     else
9017       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9018
9019     if (xx == ax && yy == ay)           // field in the middle
9020     {
9021       if (num_neighbours < life_parameter[0] ||
9022           num_neighbours > life_parameter[1])
9023       {
9024         Feld[xx][yy] = EL_EMPTY;
9025         if (Feld[xx][yy] != old_element)
9026           TEST_DrawLevelField(xx, yy);
9027         Stop[xx][yy] = TRUE;
9028         changed = TRUE;
9029       }
9030     }
9031     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
9032     {                                   // free border field
9033       if (num_neighbours >= life_parameter[2] &&
9034           num_neighbours <= life_parameter[3])
9035       {
9036         Feld[xx][yy] = element;
9037         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9038         if (Feld[xx][yy] != old_element)
9039           TEST_DrawLevelField(xx, yy);
9040         Stop[xx][yy] = TRUE;
9041         changed = TRUE;
9042       }
9043     }
9044   }
9045
9046   if (changed)
9047     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9048                    SND_GAME_OF_LIFE_GROWING);
9049 }
9050
9051 static void InitRobotWheel(int x, int y)
9052 {
9053   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9054 }
9055
9056 static void RunRobotWheel(int x, int y)
9057 {
9058   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9059 }
9060
9061 static void StopRobotWheel(int x, int y)
9062 {
9063   if (game.robot_wheel_x == x &&
9064       game.robot_wheel_y == y)
9065   {
9066     game.robot_wheel_x = -1;
9067     game.robot_wheel_y = -1;
9068     game.robot_wheel_active = FALSE;
9069   }
9070 }
9071
9072 static void InitTimegateWheel(int x, int y)
9073 {
9074   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9075 }
9076
9077 static void RunTimegateWheel(int x, int y)
9078 {
9079   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9080 }
9081
9082 static void InitMagicBallDelay(int x, int y)
9083 {
9084   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9085 }
9086
9087 static void ActivateMagicBall(int bx, int by)
9088 {
9089   int x, y;
9090
9091   if (level.ball_random)
9092   {
9093     int pos_border = RND(8);    // select one of the eight border elements
9094     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9095     int xx = pos_content % 3;
9096     int yy = pos_content / 3;
9097
9098     x = bx - 1 + xx;
9099     y = by - 1 + yy;
9100
9101     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9102       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9103   }
9104   else
9105   {
9106     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9107     {
9108       int xx = x - bx + 1;
9109       int yy = y - by + 1;
9110
9111       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9112         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9113     }
9114   }
9115
9116   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9117 }
9118
9119 static void CheckExit(int x, int y)
9120 {
9121   if (game.gems_still_needed > 0 ||
9122       game.sokoban_fields_still_needed > 0 ||
9123       game.sokoban_objects_still_needed > 0 ||
9124       game.lights_still_needed > 0)
9125   {
9126     int element = Feld[x][y];
9127     int graphic = el2img(element);
9128
9129     if (IS_ANIMATED(graphic))
9130       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9131
9132     return;
9133   }
9134
9135   // do not re-open exit door closed after last player
9136   if (game.all_players_gone)
9137     return;
9138
9139   Feld[x][y] = EL_EXIT_OPENING;
9140
9141   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9142 }
9143
9144 static void CheckExitEM(int x, int y)
9145 {
9146   if (game.gems_still_needed > 0 ||
9147       game.sokoban_fields_still_needed > 0 ||
9148       game.sokoban_objects_still_needed > 0 ||
9149       game.lights_still_needed > 0)
9150   {
9151     int element = Feld[x][y];
9152     int graphic = el2img(element);
9153
9154     if (IS_ANIMATED(graphic))
9155       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9156
9157     return;
9158   }
9159
9160   // do not re-open exit door closed after last player
9161   if (game.all_players_gone)
9162     return;
9163
9164   Feld[x][y] = EL_EM_EXIT_OPENING;
9165
9166   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9167 }
9168
9169 static void CheckExitSteel(int x, int y)
9170 {
9171   if (game.gems_still_needed > 0 ||
9172       game.sokoban_fields_still_needed > 0 ||
9173       game.sokoban_objects_still_needed > 0 ||
9174       game.lights_still_needed > 0)
9175   {
9176     int element = Feld[x][y];
9177     int graphic = el2img(element);
9178
9179     if (IS_ANIMATED(graphic))
9180       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9181
9182     return;
9183   }
9184
9185   // do not re-open exit door closed after last player
9186   if (game.all_players_gone)
9187     return;
9188
9189   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9190
9191   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9192 }
9193
9194 static void CheckExitSteelEM(int x, int y)
9195 {
9196   if (game.gems_still_needed > 0 ||
9197       game.sokoban_fields_still_needed > 0 ||
9198       game.sokoban_objects_still_needed > 0 ||
9199       game.lights_still_needed > 0)
9200   {
9201     int element = Feld[x][y];
9202     int graphic = el2img(element);
9203
9204     if (IS_ANIMATED(graphic))
9205       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9206
9207     return;
9208   }
9209
9210   // do not re-open exit door closed after last player
9211   if (game.all_players_gone)
9212     return;
9213
9214   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9215
9216   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9217 }
9218
9219 static void CheckExitSP(int x, int y)
9220 {
9221   if (game.gems_still_needed > 0)
9222   {
9223     int element = Feld[x][y];
9224     int graphic = el2img(element);
9225
9226     if (IS_ANIMATED(graphic))
9227       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9228
9229     return;
9230   }
9231
9232   // do not re-open exit door closed after last player
9233   if (game.all_players_gone)
9234     return;
9235
9236   Feld[x][y] = EL_SP_EXIT_OPENING;
9237
9238   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9239 }
9240
9241 static void CloseAllOpenTimegates(void)
9242 {
9243   int x, y;
9244
9245   SCAN_PLAYFIELD(x, y)
9246   {
9247     int element = Feld[x][y];
9248
9249     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9250     {
9251       Feld[x][y] = EL_TIMEGATE_CLOSING;
9252
9253       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9254     }
9255   }
9256 }
9257
9258 static void DrawTwinkleOnField(int x, int y)
9259 {
9260   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9261     return;
9262
9263   if (Feld[x][y] == EL_BD_DIAMOND)
9264     return;
9265
9266   if (MovDelay[x][y] == 0)      // next animation frame
9267     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9268
9269   if (MovDelay[x][y] != 0)      // wait some time before next frame
9270   {
9271     MovDelay[x][y]--;
9272
9273     DrawLevelElementAnimation(x, y, Feld[x][y]);
9274
9275     if (MovDelay[x][y] != 0)
9276     {
9277       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9278                                            10 - MovDelay[x][y]);
9279
9280       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9281     }
9282   }
9283 }
9284
9285 static void MauerWaechst(int x, int y)
9286 {
9287   int delay = 6;
9288
9289   if (!MovDelay[x][y])          // next animation frame
9290     MovDelay[x][y] = 3 * delay;
9291
9292   if (MovDelay[x][y])           // wait some time before next frame
9293   {
9294     MovDelay[x][y]--;
9295
9296     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9297     {
9298       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9299       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9300
9301       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9302     }
9303
9304     if (!MovDelay[x][y])
9305     {
9306       if (MovDir[x][y] == MV_LEFT)
9307       {
9308         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9309           TEST_DrawLevelField(x - 1, y);
9310       }
9311       else if (MovDir[x][y] == MV_RIGHT)
9312       {
9313         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9314           TEST_DrawLevelField(x + 1, y);
9315       }
9316       else if (MovDir[x][y] == MV_UP)
9317       {
9318         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9319           TEST_DrawLevelField(x, y - 1);
9320       }
9321       else
9322       {
9323         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9324           TEST_DrawLevelField(x, y + 1);
9325       }
9326
9327       Feld[x][y] = Store[x][y];
9328       Store[x][y] = 0;
9329       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9330       TEST_DrawLevelField(x, y);
9331     }
9332   }
9333 }
9334
9335 static void MauerAbleger(int ax, int ay)
9336 {
9337   int element = Feld[ax][ay];
9338   int graphic = el2img(element);
9339   boolean oben_frei = FALSE, unten_frei = FALSE;
9340   boolean links_frei = FALSE, rechts_frei = FALSE;
9341   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9342   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9343   boolean new_wall = FALSE;
9344
9345   if (IS_ANIMATED(graphic))
9346     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9347
9348   if (!MovDelay[ax][ay])        // start building new wall
9349     MovDelay[ax][ay] = 6;
9350
9351   if (MovDelay[ax][ay])         // wait some time before building new wall
9352   {
9353     MovDelay[ax][ay]--;
9354     if (MovDelay[ax][ay])
9355       return;
9356   }
9357
9358   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9359     oben_frei = TRUE;
9360   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9361     unten_frei = TRUE;
9362   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9363     links_frei = TRUE;
9364   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9365     rechts_frei = TRUE;
9366
9367   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9368       element == EL_EXPANDABLE_WALL_ANY)
9369   {
9370     if (oben_frei)
9371     {
9372       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9373       Store[ax][ay-1] = element;
9374       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9375       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9376         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9377                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9378       new_wall = TRUE;
9379     }
9380     if (unten_frei)
9381     {
9382       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9383       Store[ax][ay+1] = element;
9384       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9385       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9386         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9387                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9388       new_wall = TRUE;
9389     }
9390   }
9391
9392   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9393       element == EL_EXPANDABLE_WALL_ANY ||
9394       element == EL_EXPANDABLE_WALL ||
9395       element == EL_BD_EXPANDABLE_WALL)
9396   {
9397     if (links_frei)
9398     {
9399       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9400       Store[ax-1][ay] = element;
9401       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9402       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9403         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9404                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9405       new_wall = TRUE;
9406     }
9407
9408     if (rechts_frei)
9409     {
9410       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9411       Store[ax+1][ay] = element;
9412       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9413       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9414         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9415                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9416       new_wall = TRUE;
9417     }
9418   }
9419
9420   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9421     TEST_DrawLevelField(ax, ay);
9422
9423   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9424     oben_massiv = TRUE;
9425   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9426     unten_massiv = TRUE;
9427   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9428     links_massiv = TRUE;
9429   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9430     rechts_massiv = TRUE;
9431
9432   if (((oben_massiv && unten_massiv) ||
9433        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9434        element == EL_EXPANDABLE_WALL) &&
9435       ((links_massiv && rechts_massiv) ||
9436        element == EL_EXPANDABLE_WALL_VERTICAL))
9437     Feld[ax][ay] = EL_WALL;
9438
9439   if (new_wall)
9440     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9441 }
9442
9443 static void MauerAblegerStahl(int ax, int ay)
9444 {
9445   int element = Feld[ax][ay];
9446   int graphic = el2img(element);
9447   boolean oben_frei = FALSE, unten_frei = FALSE;
9448   boolean links_frei = FALSE, rechts_frei = FALSE;
9449   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9450   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9451   boolean new_wall = FALSE;
9452
9453   if (IS_ANIMATED(graphic))
9454     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9455
9456   if (!MovDelay[ax][ay])        // start building new wall
9457     MovDelay[ax][ay] = 6;
9458
9459   if (MovDelay[ax][ay])         // wait some time before building new wall
9460   {
9461     MovDelay[ax][ay]--;
9462     if (MovDelay[ax][ay])
9463       return;
9464   }
9465
9466   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9467     oben_frei = TRUE;
9468   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9469     unten_frei = TRUE;
9470   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9471     links_frei = TRUE;
9472   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9473     rechts_frei = TRUE;
9474
9475   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9476       element == EL_EXPANDABLE_STEELWALL_ANY)
9477   {
9478     if (oben_frei)
9479     {
9480       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9481       Store[ax][ay-1] = element;
9482       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9483       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9484         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9485                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9486       new_wall = TRUE;
9487     }
9488     if (unten_frei)
9489     {
9490       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9491       Store[ax][ay+1] = element;
9492       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9493       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9494         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9495                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9496       new_wall = TRUE;
9497     }
9498   }
9499
9500   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9501       element == EL_EXPANDABLE_STEELWALL_ANY)
9502   {
9503     if (links_frei)
9504     {
9505       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9506       Store[ax-1][ay] = element;
9507       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9508       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9509         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9510                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9511       new_wall = TRUE;
9512     }
9513
9514     if (rechts_frei)
9515     {
9516       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9517       Store[ax+1][ay] = element;
9518       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9519       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9520         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9521                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9522       new_wall = TRUE;
9523     }
9524   }
9525
9526   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9527     oben_massiv = TRUE;
9528   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9529     unten_massiv = TRUE;
9530   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9531     links_massiv = TRUE;
9532   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9533     rechts_massiv = TRUE;
9534
9535   if (((oben_massiv && unten_massiv) ||
9536        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9537       ((links_massiv && rechts_massiv) ||
9538        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9539     Feld[ax][ay] = EL_STEELWALL;
9540
9541   if (new_wall)
9542     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9543 }
9544
9545 static void CheckForDragon(int x, int y)
9546 {
9547   int i, j;
9548   boolean dragon_found = FALSE;
9549   static int xy[4][2] =
9550   {
9551     { 0, -1 },
9552     { -1, 0 },
9553     { +1, 0 },
9554     { 0, +1 }
9555   };
9556
9557   for (i = 0; i < NUM_DIRECTIONS; i++)
9558   {
9559     for (j = 0; j < 4; j++)
9560     {
9561       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9562
9563       if (IN_LEV_FIELD(xx, yy) &&
9564           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9565       {
9566         if (Feld[xx][yy] == EL_DRAGON)
9567           dragon_found = TRUE;
9568       }
9569       else
9570         break;
9571     }
9572   }
9573
9574   if (!dragon_found)
9575   {
9576     for (i = 0; i < NUM_DIRECTIONS; i++)
9577     {
9578       for (j = 0; j < 3; j++)
9579       {
9580         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9581   
9582         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9583         {
9584           Feld[xx][yy] = EL_EMPTY;
9585           TEST_DrawLevelField(xx, yy);
9586         }
9587         else
9588           break;
9589       }
9590     }
9591   }
9592 }
9593
9594 static void InitBuggyBase(int x, int y)
9595 {
9596   int element = Feld[x][y];
9597   int activating_delay = FRAMES_PER_SECOND / 4;
9598
9599   ChangeDelay[x][y] =
9600     (element == EL_SP_BUGGY_BASE ?
9601      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9602      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9603      activating_delay :
9604      element == EL_SP_BUGGY_BASE_ACTIVE ?
9605      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9606 }
9607
9608 static void WarnBuggyBase(int x, int y)
9609 {
9610   int i;
9611   static int xy[4][2] =
9612   {
9613     { 0, -1 },
9614     { -1, 0 },
9615     { +1, 0 },
9616     { 0, +1 }
9617   };
9618
9619   for (i = 0; i < NUM_DIRECTIONS; i++)
9620   {
9621     int xx = x + xy[i][0];
9622     int yy = y + xy[i][1];
9623
9624     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9625     {
9626       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9627
9628       break;
9629     }
9630   }
9631 }
9632
9633 static void InitTrap(int x, int y)
9634 {
9635   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9636 }
9637
9638 static void ActivateTrap(int x, int y)
9639 {
9640   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9641 }
9642
9643 static void ChangeActiveTrap(int x, int y)
9644 {
9645   int graphic = IMG_TRAP_ACTIVE;
9646
9647   // if new animation frame was drawn, correct crumbled sand border
9648   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9649     TEST_DrawLevelFieldCrumbled(x, y);
9650 }
9651
9652 static int getSpecialActionElement(int element, int number, int base_element)
9653 {
9654   return (element != EL_EMPTY ? element :
9655           number != -1 ? base_element + number - 1 :
9656           EL_EMPTY);
9657 }
9658
9659 static int getModifiedActionNumber(int value_old, int operator, int operand,
9660                                    int value_min, int value_max)
9661 {
9662   int value_new = (operator == CA_MODE_SET      ? operand :
9663                    operator == CA_MODE_ADD      ? value_old + operand :
9664                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9665                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9666                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9667                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9668                    value_old);
9669
9670   return (value_new < value_min ? value_min :
9671           value_new > value_max ? value_max :
9672           value_new);
9673 }
9674
9675 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9676 {
9677   struct ElementInfo *ei = &element_info[element];
9678   struct ElementChangeInfo *change = &ei->change_page[page];
9679   int target_element = change->target_element;
9680   int action_type = change->action_type;
9681   int action_mode = change->action_mode;
9682   int action_arg = change->action_arg;
9683   int action_element = change->action_element;
9684   int i;
9685
9686   if (!change->has_action)
9687     return;
9688
9689   // ---------- determine action paramater values -----------------------------
9690
9691   int level_time_value =
9692     (level.time > 0 ? TimeLeft :
9693      TimePlayed);
9694
9695   int action_arg_element_raw =
9696     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9697      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9698      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9699      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9700      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9701      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9702      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9703      EL_EMPTY);
9704   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9705
9706   int action_arg_direction =
9707     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9708      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9709      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9710      change->actual_trigger_side :
9711      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9712      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9713      MV_NONE);
9714
9715   int action_arg_number_min =
9716     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9717      CA_ARG_MIN);
9718
9719   int action_arg_number_max =
9720     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9721      action_type == CA_SET_LEVEL_GEMS ? 999 :
9722      action_type == CA_SET_LEVEL_TIME ? 9999 :
9723      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9724      action_type == CA_SET_CE_VALUE ? 9999 :
9725      action_type == CA_SET_CE_SCORE ? 9999 :
9726      CA_ARG_MAX);
9727
9728   int action_arg_number_reset =
9729     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9730      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9731      action_type == CA_SET_LEVEL_TIME ? level.time :
9732      action_type == CA_SET_LEVEL_SCORE ? 0 :
9733      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9734      action_type == CA_SET_CE_SCORE ? 0 :
9735      0);
9736
9737   int action_arg_number =
9738     (action_arg <= CA_ARG_MAX ? action_arg :
9739      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9740      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9741      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9742      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9743      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9744      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9745      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9746      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9747      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9748      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9749      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9750      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9751      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9752      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9753      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9754      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9755      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9756      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9757      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9758      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9759      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9760      -1);
9761
9762   int action_arg_number_old =
9763     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
9764      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9765      action_type == CA_SET_LEVEL_SCORE ? game.score :
9766      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9767      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9768      0);
9769
9770   int action_arg_number_new =
9771     getModifiedActionNumber(action_arg_number_old,
9772                             action_mode, action_arg_number,
9773                             action_arg_number_min, action_arg_number_max);
9774
9775   int trigger_player_bits =
9776     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9777      change->actual_trigger_player_bits : change->trigger_player);
9778
9779   int action_arg_player_bits =
9780     (action_arg >= CA_ARG_PLAYER_1 &&
9781      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9782      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9783      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9784      PLAYER_BITS_ANY);
9785
9786   // ---------- execute action  -----------------------------------------------
9787
9788   switch (action_type)
9789   {
9790     case CA_NO_ACTION:
9791     {
9792       return;
9793     }
9794
9795     // ---------- level actions  ----------------------------------------------
9796
9797     case CA_RESTART_LEVEL:
9798     {
9799       game.restart_level = TRUE;
9800
9801       break;
9802     }
9803
9804     case CA_SHOW_ENVELOPE:
9805     {
9806       int element = getSpecialActionElement(action_arg_element,
9807                                             action_arg_number, EL_ENVELOPE_1);
9808
9809       if (IS_ENVELOPE(element))
9810         local_player->show_envelope = element;
9811
9812       break;
9813     }
9814
9815     case CA_SET_LEVEL_TIME:
9816     {
9817       if (level.time > 0)       // only modify limited time value
9818       {
9819         TimeLeft = action_arg_number_new;
9820
9821         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9822
9823         DisplayGameControlValues();
9824
9825         if (!TimeLeft && setup.time_limit)
9826           for (i = 0; i < MAX_PLAYERS; i++)
9827             KillPlayer(&stored_player[i]);
9828       }
9829
9830       break;
9831     }
9832
9833     case CA_SET_LEVEL_SCORE:
9834     {
9835       game.score = action_arg_number_new;
9836
9837       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
9838
9839       DisplayGameControlValues();
9840
9841       break;
9842     }
9843
9844     case CA_SET_LEVEL_GEMS:
9845     {
9846       game.gems_still_needed = action_arg_number_new;
9847
9848       game.snapshot.collected_item = TRUE;
9849
9850       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
9851
9852       DisplayGameControlValues();
9853
9854       break;
9855     }
9856
9857     case CA_SET_LEVEL_WIND:
9858     {
9859       game.wind_direction = action_arg_direction;
9860
9861       break;
9862     }
9863
9864     case CA_SET_LEVEL_RANDOM_SEED:
9865     {
9866       // ensure that setting a new random seed while playing is predictable
9867       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9868
9869       break;
9870     }
9871
9872     // ---------- player actions  ---------------------------------------------
9873
9874     case CA_MOVE_PLAYER:
9875     {
9876       // automatically move to the next field in specified direction
9877       for (i = 0; i < MAX_PLAYERS; i++)
9878         if (trigger_player_bits & (1 << i))
9879           stored_player[i].programmed_action = action_arg_direction;
9880
9881       break;
9882     }
9883
9884     case CA_EXIT_PLAYER:
9885     {
9886       for (i = 0; i < MAX_PLAYERS; i++)
9887         if (action_arg_player_bits & (1 << i))
9888           ExitPlayer(&stored_player[i]);
9889
9890       if (game.players_still_needed == 0)
9891         LevelSolved();
9892
9893       break;
9894     }
9895
9896     case CA_KILL_PLAYER:
9897     {
9898       for (i = 0; i < MAX_PLAYERS; i++)
9899         if (action_arg_player_bits & (1 << i))
9900           KillPlayer(&stored_player[i]);
9901
9902       break;
9903     }
9904
9905     case CA_SET_PLAYER_KEYS:
9906     {
9907       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9908       int element = getSpecialActionElement(action_arg_element,
9909                                             action_arg_number, EL_KEY_1);
9910
9911       if (IS_KEY(element))
9912       {
9913         for (i = 0; i < MAX_PLAYERS; i++)
9914         {
9915           if (trigger_player_bits & (1 << i))
9916           {
9917             stored_player[i].key[KEY_NR(element)] = key_state;
9918
9919             DrawGameDoorValues();
9920           }
9921         }
9922       }
9923
9924       break;
9925     }
9926
9927     case CA_SET_PLAYER_SPEED:
9928     {
9929       for (i = 0; i < MAX_PLAYERS; i++)
9930       {
9931         if (trigger_player_bits & (1 << i))
9932         {
9933           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9934
9935           if (action_arg == CA_ARG_SPEED_FASTER &&
9936               stored_player[i].cannot_move)
9937           {
9938             action_arg_number = STEPSIZE_VERY_SLOW;
9939           }
9940           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9941                    action_arg == CA_ARG_SPEED_FASTER)
9942           {
9943             action_arg_number = 2;
9944             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9945                            CA_MODE_MULTIPLY);
9946           }
9947           else if (action_arg == CA_ARG_NUMBER_RESET)
9948           {
9949             action_arg_number = level.initial_player_stepsize[i];
9950           }
9951
9952           move_stepsize =
9953             getModifiedActionNumber(move_stepsize,
9954                                     action_mode,
9955                                     action_arg_number,
9956                                     action_arg_number_min,
9957                                     action_arg_number_max);
9958
9959           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9960         }
9961       }
9962
9963       break;
9964     }
9965
9966     case CA_SET_PLAYER_SHIELD:
9967     {
9968       for (i = 0; i < MAX_PLAYERS; i++)
9969       {
9970         if (trigger_player_bits & (1 << i))
9971         {
9972           if (action_arg == CA_ARG_SHIELD_OFF)
9973           {
9974             stored_player[i].shield_normal_time_left = 0;
9975             stored_player[i].shield_deadly_time_left = 0;
9976           }
9977           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9978           {
9979             stored_player[i].shield_normal_time_left = 999999;
9980           }
9981           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9982           {
9983             stored_player[i].shield_normal_time_left = 999999;
9984             stored_player[i].shield_deadly_time_left = 999999;
9985           }
9986         }
9987       }
9988
9989       break;
9990     }
9991
9992     case CA_SET_PLAYER_GRAVITY:
9993     {
9994       for (i = 0; i < MAX_PLAYERS; i++)
9995       {
9996         if (trigger_player_bits & (1 << i))
9997         {
9998           stored_player[i].gravity =
9999             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10000              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10001              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10002              stored_player[i].gravity);
10003         }
10004       }
10005
10006       break;
10007     }
10008
10009     case CA_SET_PLAYER_ARTWORK:
10010     {
10011       for (i = 0; i < MAX_PLAYERS; i++)
10012       {
10013         if (trigger_player_bits & (1 << i))
10014         {
10015           int artwork_element = action_arg_element;
10016
10017           if (action_arg == CA_ARG_ELEMENT_RESET)
10018             artwork_element =
10019               (level.use_artwork_element[i] ? level.artwork_element[i] :
10020                stored_player[i].element_nr);
10021
10022           if (stored_player[i].artwork_element != artwork_element)
10023             stored_player[i].Frame = 0;
10024
10025           stored_player[i].artwork_element = artwork_element;
10026
10027           SetPlayerWaiting(&stored_player[i], FALSE);
10028
10029           // set number of special actions for bored and sleeping animation
10030           stored_player[i].num_special_action_bored =
10031             get_num_special_action(artwork_element,
10032                                    ACTION_BORING_1, ACTION_BORING_LAST);
10033           stored_player[i].num_special_action_sleeping =
10034             get_num_special_action(artwork_element,
10035                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10036         }
10037       }
10038
10039       break;
10040     }
10041
10042     case CA_SET_PLAYER_INVENTORY:
10043     {
10044       for (i = 0; i < MAX_PLAYERS; i++)
10045       {
10046         struct PlayerInfo *player = &stored_player[i];
10047         int j, k;
10048
10049         if (trigger_player_bits & (1 << i))
10050         {
10051           int inventory_element = action_arg_element;
10052
10053           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10054               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10055               action_arg == CA_ARG_ELEMENT_ACTION)
10056           {
10057             int element = inventory_element;
10058             int collect_count = element_info[element].collect_count_initial;
10059
10060             if (!IS_CUSTOM_ELEMENT(element))
10061               collect_count = 1;
10062
10063             if (collect_count == 0)
10064               player->inventory_infinite_element = element;
10065             else
10066               for (k = 0; k < collect_count; k++)
10067                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10068                   player->inventory_element[player->inventory_size++] =
10069                     element;
10070           }
10071           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10072                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10073                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10074           {
10075             if (player->inventory_infinite_element != EL_UNDEFINED &&
10076                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10077                                      action_arg_element_raw))
10078               player->inventory_infinite_element = EL_UNDEFINED;
10079
10080             for (k = 0, j = 0; j < player->inventory_size; j++)
10081             {
10082               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10083                                         action_arg_element_raw))
10084                 player->inventory_element[k++] = player->inventory_element[j];
10085             }
10086
10087             player->inventory_size = k;
10088           }
10089           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10090           {
10091             if (player->inventory_size > 0)
10092             {
10093               for (j = 0; j < player->inventory_size - 1; j++)
10094                 player->inventory_element[j] = player->inventory_element[j + 1];
10095
10096               player->inventory_size--;
10097             }
10098           }
10099           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10100           {
10101             if (player->inventory_size > 0)
10102               player->inventory_size--;
10103           }
10104           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10105           {
10106             player->inventory_infinite_element = EL_UNDEFINED;
10107             player->inventory_size = 0;
10108           }
10109           else if (action_arg == CA_ARG_INVENTORY_RESET)
10110           {
10111             player->inventory_infinite_element = EL_UNDEFINED;
10112             player->inventory_size = 0;
10113
10114             if (level.use_initial_inventory[i])
10115             {
10116               for (j = 0; j < level.initial_inventory_size[i]; j++)
10117               {
10118                 int element = level.initial_inventory_content[i][j];
10119                 int collect_count = element_info[element].collect_count_initial;
10120
10121                 if (!IS_CUSTOM_ELEMENT(element))
10122                   collect_count = 1;
10123
10124                 if (collect_count == 0)
10125                   player->inventory_infinite_element = element;
10126                 else
10127                   for (k = 0; k < collect_count; k++)
10128                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10129                       player->inventory_element[player->inventory_size++] =
10130                         element;
10131               }
10132             }
10133           }
10134         }
10135       }
10136
10137       break;
10138     }
10139
10140     // ---------- CE actions  -------------------------------------------------
10141
10142     case CA_SET_CE_VALUE:
10143     {
10144       int last_ce_value = CustomValue[x][y];
10145
10146       CustomValue[x][y] = action_arg_number_new;
10147
10148       if (CustomValue[x][y] != last_ce_value)
10149       {
10150         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10151         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10152
10153         if (CustomValue[x][y] == 0)
10154         {
10155           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10156           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10157         }
10158       }
10159
10160       break;
10161     }
10162
10163     case CA_SET_CE_SCORE:
10164     {
10165       int last_ce_score = ei->collect_score;
10166
10167       ei->collect_score = action_arg_number_new;
10168
10169       if (ei->collect_score != last_ce_score)
10170       {
10171         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10172         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10173
10174         if (ei->collect_score == 0)
10175         {
10176           int xx, yy;
10177
10178           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10179           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10180
10181           /*
10182             This is a very special case that seems to be a mixture between
10183             CheckElementChange() and CheckTriggeredElementChange(): while
10184             the first one only affects single elements that are triggered
10185             directly, the second one affects multiple elements in the playfield
10186             that are triggered indirectly by another element. This is a third
10187             case: Changing the CE score always affects multiple identical CEs,
10188             so every affected CE must be checked, not only the single CE for
10189             which the CE score was changed in the first place (as every instance
10190             of that CE shares the same CE score, and therefore also can change)!
10191           */
10192           SCAN_PLAYFIELD(xx, yy)
10193           {
10194             if (Feld[xx][yy] == element)
10195               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10196                                  CE_SCORE_GETS_ZERO);
10197           }
10198         }
10199       }
10200
10201       break;
10202     }
10203
10204     case CA_SET_CE_ARTWORK:
10205     {
10206       int artwork_element = action_arg_element;
10207       boolean reset_frame = FALSE;
10208       int xx, yy;
10209
10210       if (action_arg == CA_ARG_ELEMENT_RESET)
10211         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10212                            element);
10213
10214       if (ei->gfx_element != artwork_element)
10215         reset_frame = TRUE;
10216
10217       ei->gfx_element = artwork_element;
10218
10219       SCAN_PLAYFIELD(xx, yy)
10220       {
10221         if (Feld[xx][yy] == element)
10222         {
10223           if (reset_frame)
10224           {
10225             ResetGfxAnimation(xx, yy);
10226             ResetRandomAnimationValue(xx, yy);
10227           }
10228
10229           TEST_DrawLevelField(xx, yy);
10230         }
10231       }
10232
10233       break;
10234     }
10235
10236     // ---------- engine actions  ---------------------------------------------
10237
10238     case CA_SET_ENGINE_SCAN_MODE:
10239     {
10240       InitPlayfieldScanMode(action_arg);
10241
10242       break;
10243     }
10244
10245     default:
10246       break;
10247   }
10248 }
10249
10250 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10251 {
10252   int old_element = Feld[x][y];
10253   int new_element = GetElementFromGroupElement(element);
10254   int previous_move_direction = MovDir[x][y];
10255   int last_ce_value = CustomValue[x][y];
10256   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10257   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10258   boolean add_player_onto_element = (new_element_is_player &&
10259                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10260                                      IS_WALKABLE(old_element));
10261
10262   if (!add_player_onto_element)
10263   {
10264     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10265       RemoveMovingField(x, y);
10266     else
10267       RemoveField(x, y);
10268
10269     Feld[x][y] = new_element;
10270
10271     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10272       MovDir[x][y] = previous_move_direction;
10273
10274     if (element_info[new_element].use_last_ce_value)
10275       CustomValue[x][y] = last_ce_value;
10276
10277     InitField_WithBug1(x, y, FALSE);
10278
10279     new_element = Feld[x][y];   // element may have changed
10280
10281     ResetGfxAnimation(x, y);
10282     ResetRandomAnimationValue(x, y);
10283
10284     TEST_DrawLevelField(x, y);
10285
10286     if (GFX_CRUMBLED(new_element))
10287       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10288   }
10289
10290   // check if element under the player changes from accessible to unaccessible
10291   // (needed for special case of dropping element which then changes)
10292   // (must be checked after creating new element for walkable group elements)
10293   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10294       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10295   {
10296     Bang(x, y);
10297
10298     return;
10299   }
10300
10301   // "ChangeCount" not set yet to allow "entered by player" change one time
10302   if (new_element_is_player)
10303     RelocatePlayer(x, y, new_element);
10304
10305   if (is_change)
10306     ChangeCount[x][y]++;        // count number of changes in the same frame
10307
10308   TestIfBadThingTouchesPlayer(x, y);
10309   TestIfPlayerTouchesCustomElement(x, y);
10310   TestIfElementTouchesCustomElement(x, y);
10311 }
10312
10313 static void CreateField(int x, int y, int element)
10314 {
10315   CreateFieldExt(x, y, element, FALSE);
10316 }
10317
10318 static void CreateElementFromChange(int x, int y, int element)
10319 {
10320   element = GET_VALID_RUNTIME_ELEMENT(element);
10321
10322   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10323   {
10324     int old_element = Feld[x][y];
10325
10326     // prevent changed element from moving in same engine frame
10327     // unless both old and new element can either fall or move
10328     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10329         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10330       Stop[x][y] = TRUE;
10331   }
10332
10333   CreateFieldExt(x, y, element, TRUE);
10334 }
10335
10336 static boolean ChangeElement(int x, int y, int element, int page)
10337 {
10338   struct ElementInfo *ei = &element_info[element];
10339   struct ElementChangeInfo *change = &ei->change_page[page];
10340   int ce_value = CustomValue[x][y];
10341   int ce_score = ei->collect_score;
10342   int target_element;
10343   int old_element = Feld[x][y];
10344
10345   // always use default change event to prevent running into a loop
10346   if (ChangeEvent[x][y] == -1)
10347     ChangeEvent[x][y] = CE_DELAY;
10348
10349   if (ChangeEvent[x][y] == CE_DELAY)
10350   {
10351     // reset actual trigger element, trigger player and action element
10352     change->actual_trigger_element = EL_EMPTY;
10353     change->actual_trigger_player = EL_EMPTY;
10354     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10355     change->actual_trigger_side = CH_SIDE_NONE;
10356     change->actual_trigger_ce_value = 0;
10357     change->actual_trigger_ce_score = 0;
10358   }
10359
10360   // do not change elements more than a specified maximum number of changes
10361   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10362     return FALSE;
10363
10364   ChangeCount[x][y]++;          // count number of changes in the same frame
10365
10366   if (change->explode)
10367   {
10368     Bang(x, y);
10369
10370     return TRUE;
10371   }
10372
10373   if (change->use_target_content)
10374   {
10375     boolean complete_replace = TRUE;
10376     boolean can_replace[3][3];
10377     int xx, yy;
10378
10379     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10380     {
10381       boolean is_empty;
10382       boolean is_walkable;
10383       boolean is_diggable;
10384       boolean is_collectible;
10385       boolean is_removable;
10386       boolean is_destructible;
10387       int ex = x + xx - 1;
10388       int ey = y + yy - 1;
10389       int content_element = change->target_content.e[xx][yy];
10390       int e;
10391
10392       can_replace[xx][yy] = TRUE;
10393
10394       if (ex == x && ey == y)   // do not check changing element itself
10395         continue;
10396
10397       if (content_element == EL_EMPTY_SPACE)
10398       {
10399         can_replace[xx][yy] = FALSE;    // do not replace border with space
10400
10401         continue;
10402       }
10403
10404       if (!IN_LEV_FIELD(ex, ey))
10405       {
10406         can_replace[xx][yy] = FALSE;
10407         complete_replace = FALSE;
10408
10409         continue;
10410       }
10411
10412       e = Feld[ex][ey];
10413
10414       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10415         e = MovingOrBlocked2Element(ex, ey);
10416
10417       is_empty = (IS_FREE(ex, ey) ||
10418                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10419
10420       is_walkable     = (is_empty || IS_WALKABLE(e));
10421       is_diggable     = (is_empty || IS_DIGGABLE(e));
10422       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10423       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10424       is_removable    = (is_diggable || is_collectible);
10425
10426       can_replace[xx][yy] =
10427         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10428           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10429           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10430           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10431           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10432           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10433          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10434
10435       if (!can_replace[xx][yy])
10436         complete_replace = FALSE;
10437     }
10438
10439     if (!change->only_if_complete || complete_replace)
10440     {
10441       boolean something_has_changed = FALSE;
10442
10443       if (change->only_if_complete && change->use_random_replace &&
10444           RND(100) < change->random_percentage)
10445         return FALSE;
10446
10447       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10448       {
10449         int ex = x + xx - 1;
10450         int ey = y + yy - 1;
10451         int content_element;
10452
10453         if (can_replace[xx][yy] && (!change->use_random_replace ||
10454                                     RND(100) < change->random_percentage))
10455         {
10456           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10457             RemoveMovingField(ex, ey);
10458
10459           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10460
10461           content_element = change->target_content.e[xx][yy];
10462           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10463                                               ce_value, ce_score);
10464
10465           CreateElementFromChange(ex, ey, target_element);
10466
10467           something_has_changed = TRUE;
10468
10469           // for symmetry reasons, freeze newly created border elements
10470           if (ex != x || ey != y)
10471             Stop[ex][ey] = TRUE;        // no more moving in this frame
10472         }
10473       }
10474
10475       if (something_has_changed)
10476       {
10477         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10478         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10479       }
10480     }
10481   }
10482   else
10483   {
10484     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10485                                         ce_value, ce_score);
10486
10487     if (element == EL_DIAGONAL_GROWING ||
10488         element == EL_DIAGONAL_SHRINKING)
10489     {
10490       target_element = Store[x][y];
10491
10492       Store[x][y] = EL_EMPTY;
10493     }
10494
10495     CreateElementFromChange(x, y, target_element);
10496
10497     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10498     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10499   }
10500
10501   // this uses direct change before indirect change
10502   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10503
10504   return TRUE;
10505 }
10506
10507 static void HandleElementChange(int x, int y, int page)
10508 {
10509   int element = MovingOrBlocked2Element(x, y);
10510   struct ElementInfo *ei = &element_info[element];
10511   struct ElementChangeInfo *change = &ei->change_page[page];
10512   boolean handle_action_before_change = FALSE;
10513
10514 #ifdef DEBUG
10515   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10516       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10517   {
10518     printf("\n\n");
10519     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10520            x, y, element, element_info[element].token_name);
10521     printf("HandleElementChange(): This should never happen!\n");
10522     printf("\n\n");
10523   }
10524 #endif
10525
10526   // this can happen with classic bombs on walkable, changing elements
10527   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10528   {
10529     return;
10530   }
10531
10532   if (ChangeDelay[x][y] == 0)           // initialize element change
10533   {
10534     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10535
10536     if (change->can_change)
10537     {
10538       // !!! not clear why graphic animation should be reset at all here !!!
10539       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10540       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10541
10542       /*
10543         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10544
10545         When using an animation frame delay of 1 (this only happens with
10546         "sp_zonk.moving.left/right" in the classic graphics), the default
10547         (non-moving) animation shows wrong animation frames (while the
10548         moving animation, like "sp_zonk.moving.left/right", is correct,
10549         so this graphical bug never shows up with the classic graphics).
10550         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10551         be drawn instead of the correct frames 0,1,2,3. This is caused by
10552         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10553         an element change: First when the change delay ("ChangeDelay[][]")
10554         counter has reached zero after decrementing, then a second time in
10555         the next frame (after "GfxFrame[][]" was already incremented) when
10556         "ChangeDelay[][]" is reset to the initial delay value again.
10557
10558         This causes frame 0 to be drawn twice, while the last frame won't
10559         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10560
10561         As some animations may already be cleverly designed around this bug
10562         (at least the "Snake Bite" snake tail animation does this), it cannot
10563         simply be fixed here without breaking such existing animations.
10564         Unfortunately, it cannot easily be detected if a graphics set was
10565         designed "before" or "after" the bug was fixed. As a workaround,
10566         a new graphics set option "game.graphics_engine_version" was added
10567         to be able to specify the game's major release version for which the
10568         graphics set was designed, which can then be used to decide if the
10569         bugfix should be used (version 4 and above) or not (version 3 or
10570         below, or if no version was specified at all, as with old sets).
10571
10572         (The wrong/fixed animation frames can be tested with the test level set
10573         "test_gfxframe" and level "000", which contains a specially prepared
10574         custom element at level position (x/y) == (11/9) which uses the zonk
10575         animation mentioned above. Using "game.graphics_engine_version: 4"
10576         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10577         This can also be seen from the debug output for this test element.)
10578       */
10579
10580       // when a custom element is about to change (for example by change delay),
10581       // do not reset graphic animation when the custom element is moving
10582       if (game.graphics_engine_version < 4 &&
10583           !IS_MOVING(x, y))
10584       {
10585         ResetGfxAnimation(x, y);
10586         ResetRandomAnimationValue(x, y);
10587       }
10588
10589       if (change->pre_change_function)
10590         change->pre_change_function(x, y);
10591     }
10592   }
10593
10594   ChangeDelay[x][y]--;
10595
10596   if (ChangeDelay[x][y] != 0)           // continue element change
10597   {
10598     if (change->can_change)
10599     {
10600       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10601
10602       if (IS_ANIMATED(graphic))
10603         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10604
10605       if (change->change_function)
10606         change->change_function(x, y);
10607     }
10608   }
10609   else                                  // finish element change
10610   {
10611     if (ChangePage[x][y] != -1)         // remember page from delayed change
10612     {
10613       page = ChangePage[x][y];
10614       ChangePage[x][y] = -1;
10615
10616       change = &ei->change_page[page];
10617     }
10618
10619     if (IS_MOVING(x, y))                // never change a running system ;-)
10620     {
10621       ChangeDelay[x][y] = 1;            // try change after next move step
10622       ChangePage[x][y] = page;          // remember page to use for change
10623
10624       return;
10625     }
10626
10627     // special case: set new level random seed before changing element
10628     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10629       handle_action_before_change = TRUE;
10630
10631     if (change->has_action && handle_action_before_change)
10632       ExecuteCustomElementAction(x, y, element, page);
10633
10634     if (change->can_change)
10635     {
10636       if (ChangeElement(x, y, element, page))
10637       {
10638         if (change->post_change_function)
10639           change->post_change_function(x, y);
10640       }
10641     }
10642
10643     if (change->has_action && !handle_action_before_change)
10644       ExecuteCustomElementAction(x, y, element, page);
10645   }
10646 }
10647
10648 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10649                                               int trigger_element,
10650                                               int trigger_event,
10651                                               int trigger_player,
10652                                               int trigger_side,
10653                                               int trigger_page)
10654 {
10655   boolean change_done_any = FALSE;
10656   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10657   int i;
10658
10659   if (!(trigger_events[trigger_element][trigger_event]))
10660     return FALSE;
10661
10662   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10663
10664   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10665   {
10666     int element = EL_CUSTOM_START + i;
10667     boolean change_done = FALSE;
10668     int p;
10669
10670     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10671         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10672       continue;
10673
10674     for (p = 0; p < element_info[element].num_change_pages; p++)
10675     {
10676       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10677
10678       if (change->can_change_or_has_action &&
10679           change->has_event[trigger_event] &&
10680           change->trigger_side & trigger_side &&
10681           change->trigger_player & trigger_player &&
10682           change->trigger_page & trigger_page_bits &&
10683           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10684       {
10685         change->actual_trigger_element = trigger_element;
10686         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10687         change->actual_trigger_player_bits = trigger_player;
10688         change->actual_trigger_side = trigger_side;
10689         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10690         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10691
10692         if ((change->can_change && !change_done) || change->has_action)
10693         {
10694           int x, y;
10695
10696           SCAN_PLAYFIELD(x, y)
10697           {
10698             if (Feld[x][y] == element)
10699             {
10700               if (change->can_change && !change_done)
10701               {
10702                 // if element already changed in this frame, not only prevent
10703                 // another element change (checked in ChangeElement()), but
10704                 // also prevent additional element actions for this element
10705
10706                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10707                     !level.use_action_after_change_bug)
10708                   continue;
10709
10710                 ChangeDelay[x][y] = 1;
10711                 ChangeEvent[x][y] = trigger_event;
10712
10713                 HandleElementChange(x, y, p);
10714               }
10715               else if (change->has_action)
10716               {
10717                 // if element already changed in this frame, not only prevent
10718                 // another element change (checked in ChangeElement()), but
10719                 // also prevent additional element actions for this element
10720
10721                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10722                     !level.use_action_after_change_bug)
10723                   continue;
10724
10725                 ExecuteCustomElementAction(x, y, element, p);
10726                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10727               }
10728             }
10729           }
10730
10731           if (change->can_change)
10732           {
10733             change_done = TRUE;
10734             change_done_any = TRUE;
10735           }
10736         }
10737       }
10738     }
10739   }
10740
10741   RECURSION_LOOP_DETECTION_END();
10742
10743   return change_done_any;
10744 }
10745
10746 static boolean CheckElementChangeExt(int x, int y,
10747                                      int element,
10748                                      int trigger_element,
10749                                      int trigger_event,
10750                                      int trigger_player,
10751                                      int trigger_side)
10752 {
10753   boolean change_done = FALSE;
10754   int p;
10755
10756   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10757       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10758     return FALSE;
10759
10760   if (Feld[x][y] == EL_BLOCKED)
10761   {
10762     Blocked2Moving(x, y, &x, &y);
10763     element = Feld[x][y];
10764   }
10765
10766   // check if element has already changed or is about to change after moving
10767   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10768        Feld[x][y] != element) ||
10769
10770       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10771        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10772         ChangePage[x][y] != -1)))
10773     return FALSE;
10774
10775   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10776
10777   for (p = 0; p < element_info[element].num_change_pages; p++)
10778   {
10779     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10780
10781     /* check trigger element for all events where the element that is checked
10782        for changing interacts with a directly adjacent element -- this is
10783        different to element changes that affect other elements to change on the
10784        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10785     boolean check_trigger_element =
10786       (trigger_event == CE_TOUCHING_X ||
10787        trigger_event == CE_HITTING_X ||
10788        trigger_event == CE_HIT_BY_X ||
10789        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
10790
10791     if (change->can_change_or_has_action &&
10792         change->has_event[trigger_event] &&
10793         change->trigger_side & trigger_side &&
10794         change->trigger_player & trigger_player &&
10795         (!check_trigger_element ||
10796          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10797     {
10798       change->actual_trigger_element = trigger_element;
10799       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10800       change->actual_trigger_player_bits = trigger_player;
10801       change->actual_trigger_side = trigger_side;
10802       change->actual_trigger_ce_value = CustomValue[x][y];
10803       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10804
10805       // special case: trigger element not at (x,y) position for some events
10806       if (check_trigger_element)
10807       {
10808         static struct
10809         {
10810           int dx, dy;
10811         } move_xy[] =
10812           {
10813             {  0,  0 },
10814             { -1,  0 },
10815             { +1,  0 },
10816             {  0,  0 },
10817             {  0, -1 },
10818             {  0,  0 }, { 0, 0 }, { 0, 0 },
10819             {  0, +1 }
10820           };
10821
10822         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10823         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10824
10825         change->actual_trigger_ce_value = CustomValue[xx][yy];
10826         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10827       }
10828
10829       if (change->can_change && !change_done)
10830       {
10831         ChangeDelay[x][y] = 1;
10832         ChangeEvent[x][y] = trigger_event;
10833
10834         HandleElementChange(x, y, p);
10835
10836         change_done = TRUE;
10837       }
10838       else if (change->has_action)
10839       {
10840         ExecuteCustomElementAction(x, y, element, p);
10841         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10842       }
10843     }
10844   }
10845
10846   RECURSION_LOOP_DETECTION_END();
10847
10848   return change_done;
10849 }
10850
10851 static void PlayPlayerSound(struct PlayerInfo *player)
10852 {
10853   int jx = player->jx, jy = player->jy;
10854   int sound_element = player->artwork_element;
10855   int last_action = player->last_action_waiting;
10856   int action = player->action_waiting;
10857
10858   if (player->is_waiting)
10859   {
10860     if (action != last_action)
10861       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10862     else
10863       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10864   }
10865   else
10866   {
10867     if (action != last_action)
10868       StopSound(element_info[sound_element].sound[last_action]);
10869
10870     if (last_action == ACTION_SLEEPING)
10871       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10872   }
10873 }
10874
10875 static void PlayAllPlayersSound(void)
10876 {
10877   int i;
10878
10879   for (i = 0; i < MAX_PLAYERS; i++)
10880     if (stored_player[i].active)
10881       PlayPlayerSound(&stored_player[i]);
10882 }
10883
10884 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10885 {
10886   boolean last_waiting = player->is_waiting;
10887   int move_dir = player->MovDir;
10888
10889   player->dir_waiting = move_dir;
10890   player->last_action_waiting = player->action_waiting;
10891
10892   if (is_waiting)
10893   {
10894     if (!last_waiting)          // not waiting -> waiting
10895     {
10896       player->is_waiting = TRUE;
10897
10898       player->frame_counter_bored =
10899         FrameCounter +
10900         game.player_boring_delay_fixed +
10901         GetSimpleRandom(game.player_boring_delay_random);
10902       player->frame_counter_sleeping =
10903         FrameCounter +
10904         game.player_sleeping_delay_fixed +
10905         GetSimpleRandom(game.player_sleeping_delay_random);
10906
10907       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10908     }
10909
10910     if (game.player_sleeping_delay_fixed +
10911         game.player_sleeping_delay_random > 0 &&
10912         player->anim_delay_counter == 0 &&
10913         player->post_delay_counter == 0 &&
10914         FrameCounter >= player->frame_counter_sleeping)
10915       player->is_sleeping = TRUE;
10916     else if (game.player_boring_delay_fixed +
10917              game.player_boring_delay_random > 0 &&
10918              FrameCounter >= player->frame_counter_bored)
10919       player->is_bored = TRUE;
10920
10921     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10922                               player->is_bored ? ACTION_BORING :
10923                               ACTION_WAITING);
10924
10925     if (player->is_sleeping && player->use_murphy)
10926     {
10927       // special case for sleeping Murphy when leaning against non-free tile
10928
10929       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10930           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10931            !IS_MOVING(player->jx - 1, player->jy)))
10932         move_dir = MV_LEFT;
10933       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10934                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10935                 !IS_MOVING(player->jx + 1, player->jy)))
10936         move_dir = MV_RIGHT;
10937       else
10938         player->is_sleeping = FALSE;
10939
10940       player->dir_waiting = move_dir;
10941     }
10942
10943     if (player->is_sleeping)
10944     {
10945       if (player->num_special_action_sleeping > 0)
10946       {
10947         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10948         {
10949           int last_special_action = player->special_action_sleeping;
10950           int num_special_action = player->num_special_action_sleeping;
10951           int special_action =
10952             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10953              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10954              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10955              last_special_action + 1 : ACTION_SLEEPING);
10956           int special_graphic =
10957             el_act_dir2img(player->artwork_element, special_action, move_dir);
10958
10959           player->anim_delay_counter =
10960             graphic_info[special_graphic].anim_delay_fixed +
10961             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10962           player->post_delay_counter =
10963             graphic_info[special_graphic].post_delay_fixed +
10964             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10965
10966           player->special_action_sleeping = special_action;
10967         }
10968
10969         if (player->anim_delay_counter > 0)
10970         {
10971           player->action_waiting = player->special_action_sleeping;
10972           player->anim_delay_counter--;
10973         }
10974         else if (player->post_delay_counter > 0)
10975         {
10976           player->post_delay_counter--;
10977         }
10978       }
10979     }
10980     else if (player->is_bored)
10981     {
10982       if (player->num_special_action_bored > 0)
10983       {
10984         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10985         {
10986           int special_action =
10987             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10988           int special_graphic =
10989             el_act_dir2img(player->artwork_element, special_action, move_dir);
10990
10991           player->anim_delay_counter =
10992             graphic_info[special_graphic].anim_delay_fixed +
10993             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10994           player->post_delay_counter =
10995             graphic_info[special_graphic].post_delay_fixed +
10996             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10997
10998           player->special_action_bored = special_action;
10999         }
11000
11001         if (player->anim_delay_counter > 0)
11002         {
11003           player->action_waiting = player->special_action_bored;
11004           player->anim_delay_counter--;
11005         }
11006         else if (player->post_delay_counter > 0)
11007         {
11008           player->post_delay_counter--;
11009         }
11010       }
11011     }
11012   }
11013   else if (last_waiting)        // waiting -> not waiting
11014   {
11015     player->is_waiting = FALSE;
11016     player->is_bored = FALSE;
11017     player->is_sleeping = FALSE;
11018
11019     player->frame_counter_bored = -1;
11020     player->frame_counter_sleeping = -1;
11021
11022     player->anim_delay_counter = 0;
11023     player->post_delay_counter = 0;
11024
11025     player->dir_waiting = player->MovDir;
11026     player->action_waiting = ACTION_DEFAULT;
11027
11028     player->special_action_bored = ACTION_DEFAULT;
11029     player->special_action_sleeping = ACTION_DEFAULT;
11030   }
11031 }
11032
11033 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11034 {
11035   if ((!player->is_moving  && player->was_moving) ||
11036       (player->MovPos == 0 && player->was_moving) ||
11037       (player->is_snapping && !player->was_snapping) ||
11038       (player->is_dropping && !player->was_dropping))
11039   {
11040     if (!CheckSaveEngineSnapshotToList())
11041       return;
11042
11043     player->was_moving = FALSE;
11044     player->was_snapping = TRUE;
11045     player->was_dropping = TRUE;
11046   }
11047   else
11048   {
11049     if (player->is_moving)
11050       player->was_moving = TRUE;
11051
11052     if (!player->is_snapping)
11053       player->was_snapping = FALSE;
11054
11055     if (!player->is_dropping)
11056       player->was_dropping = FALSE;
11057   }
11058 }
11059
11060 static void CheckSingleStepMode(struct PlayerInfo *player)
11061 {
11062   if (tape.single_step && tape.recording && !tape.pausing)
11063   {
11064     /* as it is called "single step mode", just return to pause mode when the
11065        player stopped moving after one tile (or never starts moving at all) */
11066     if (!player->is_moving &&
11067         !player->is_pushing &&
11068         !player->is_dropping_pressed)
11069       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11070   }
11071
11072   CheckSaveEngineSnapshot(player);
11073 }
11074
11075 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11076 {
11077   int left      = player_action & JOY_LEFT;
11078   int right     = player_action & JOY_RIGHT;
11079   int up        = player_action & JOY_UP;
11080   int down      = player_action & JOY_DOWN;
11081   int button1   = player_action & JOY_BUTTON_1;
11082   int button2   = player_action & JOY_BUTTON_2;
11083   int dx        = (left ? -1 : right ? 1 : 0);
11084   int dy        = (up   ? -1 : down  ? 1 : 0);
11085
11086   if (!player->active || tape.pausing)
11087     return 0;
11088
11089   if (player_action)
11090   {
11091     if (button1)
11092       SnapField(player, dx, dy);
11093     else
11094     {
11095       if (button2)
11096         DropElement(player);
11097
11098       MovePlayer(player, dx, dy);
11099     }
11100
11101     CheckSingleStepMode(player);
11102
11103     SetPlayerWaiting(player, FALSE);
11104
11105     return player_action;
11106   }
11107   else
11108   {
11109     // no actions for this player (no input at player's configured device)
11110
11111     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11112     SnapField(player, 0, 0);
11113     CheckGravityMovementWhenNotMoving(player);
11114
11115     if (player->MovPos == 0)
11116       SetPlayerWaiting(player, TRUE);
11117
11118     if (player->MovPos == 0)    // needed for tape.playing
11119       player->is_moving = FALSE;
11120
11121     player->is_dropping = FALSE;
11122     player->is_dropping_pressed = FALSE;
11123     player->drop_pressed_delay = 0;
11124
11125     CheckSingleStepMode(player);
11126
11127     return 0;
11128   }
11129 }
11130
11131 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11132                                          byte *tape_action)
11133 {
11134   if (!tape.use_mouse)
11135     return;
11136
11137   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11138   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11139   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11140 }
11141
11142 static void SetTapeActionFromMouseAction(byte *tape_action,
11143                                          struct MouseActionInfo *mouse_action)
11144 {
11145   if (!tape.use_mouse)
11146     return;
11147
11148   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11149   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11150   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11151 }
11152
11153 static void CheckLevelSolved(void)
11154 {
11155   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11156   {
11157     if (game_em.level_solved &&
11158         !game_em.game_over)                             // game won
11159     {
11160       LevelSolved();
11161
11162       game_em.game_over = TRUE;
11163
11164       game.all_players_gone = TRUE;
11165     }
11166
11167     if (game_em.game_over)                              // game lost
11168       game.all_players_gone = TRUE;
11169   }
11170   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11171   {
11172     if (game_sp.level_solved &&
11173         !game_sp.game_over)                             // game won
11174     {
11175       LevelSolved();
11176
11177       game_sp.game_over = TRUE;
11178
11179       game.all_players_gone = TRUE;
11180     }
11181
11182     if (game_sp.game_over)                              // game lost
11183       game.all_players_gone = TRUE;
11184   }
11185   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11186   {
11187     if (game_mm.level_solved &&
11188         !game_mm.game_over)                             // game won
11189     {
11190       LevelSolved();
11191
11192       game_mm.game_over = TRUE;
11193
11194       game.all_players_gone = TRUE;
11195     }
11196
11197     if (game_mm.game_over)                              // game lost
11198       game.all_players_gone = TRUE;
11199   }
11200 }
11201
11202 static void CheckLevelTime(void)
11203 {
11204   int i;
11205
11206   if (TimeFrames >= FRAMES_PER_SECOND)
11207   {
11208     TimeFrames = 0;
11209     TapeTime++;
11210
11211     for (i = 0; i < MAX_PLAYERS; i++)
11212     {
11213       struct PlayerInfo *player = &stored_player[i];
11214
11215       if (SHIELD_ON(player))
11216       {
11217         player->shield_normal_time_left--;
11218
11219         if (player->shield_deadly_time_left > 0)
11220           player->shield_deadly_time_left--;
11221       }
11222     }
11223
11224     if (!game.LevelSolved && !level.use_step_counter)
11225     {
11226       TimePlayed++;
11227
11228       if (TimeLeft > 0)
11229       {
11230         TimeLeft--;
11231
11232         if (TimeLeft <= 10 && setup.time_limit)
11233           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11234
11235         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11236            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11237
11238         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11239
11240         if (!TimeLeft && setup.time_limit)
11241         {
11242           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11243             level.native_em_level->lev->killed_out_of_time = TRUE;
11244           else
11245             for (i = 0; i < MAX_PLAYERS; i++)
11246               KillPlayer(&stored_player[i]);
11247         }
11248       }
11249       else if (game.no_time_limit && !game.all_players_gone)
11250       {
11251         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11252       }
11253
11254       level.native_em_level->lev->time =
11255         (game.no_time_limit ? TimePlayed : TimeLeft);
11256     }
11257
11258     if (tape.recording || tape.playing)
11259       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11260   }
11261
11262   if (tape.recording || tape.playing)
11263     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11264
11265   UpdateAndDisplayGameControlValues();
11266 }
11267
11268 void AdvanceFrameAndPlayerCounters(int player_nr)
11269 {
11270   int i;
11271
11272   // advance frame counters (global frame counter and time frame counter)
11273   FrameCounter++;
11274   TimeFrames++;
11275
11276   // advance player counters (counters for move delay, move animation etc.)
11277   for (i = 0; i < MAX_PLAYERS; i++)
11278   {
11279     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11280     int move_delay_value = stored_player[i].move_delay_value;
11281     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11282
11283     if (!advance_player_counters)       // not all players may be affected
11284       continue;
11285
11286     if (move_frames == 0)       // less than one move per game frame
11287     {
11288       int stepsize = TILEX / move_delay_value;
11289       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11290       int count = (stored_player[i].is_moving ?
11291                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11292
11293       if (count % delay == 0)
11294         move_frames = 1;
11295     }
11296
11297     stored_player[i].Frame += move_frames;
11298
11299     if (stored_player[i].MovPos != 0)
11300       stored_player[i].StepFrame += move_frames;
11301
11302     if (stored_player[i].move_delay > 0)
11303       stored_player[i].move_delay--;
11304
11305     // due to bugs in previous versions, counter must count up, not down
11306     if (stored_player[i].push_delay != -1)
11307       stored_player[i].push_delay++;
11308
11309     if (stored_player[i].drop_delay > 0)
11310       stored_player[i].drop_delay--;
11311
11312     if (stored_player[i].is_dropping_pressed)
11313       stored_player[i].drop_pressed_delay++;
11314   }
11315 }
11316
11317 void StartGameActions(boolean init_network_game, boolean record_tape,
11318                       int random_seed)
11319 {
11320   unsigned int new_random_seed = InitRND(random_seed);
11321
11322   if (record_tape)
11323     TapeStartRecording(new_random_seed);
11324
11325   if (init_network_game)
11326   {
11327     SendToServer_LevelFile();
11328     SendToServer_StartPlaying();
11329
11330     return;
11331   }
11332
11333   InitGame();
11334 }
11335
11336 static void GameActionsExt(void)
11337 {
11338 #if 0
11339   static unsigned int game_frame_delay = 0;
11340 #endif
11341   unsigned int game_frame_delay_value;
11342   byte *recorded_player_action;
11343   byte summarized_player_action = 0;
11344   byte tape_action[MAX_PLAYERS];
11345   int i;
11346
11347   // detect endless loops, caused by custom element programming
11348   if (recursion_loop_detected && recursion_loop_depth == 0)
11349   {
11350     char *message = getStringCat3("Internal Error! Element ",
11351                                   EL_NAME(recursion_loop_element),
11352                                   " caused endless loop! Quit the game?");
11353
11354     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11355           EL_NAME(recursion_loop_element));
11356
11357     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11358
11359     recursion_loop_detected = FALSE;    // if game should be continued
11360
11361     free(message);
11362
11363     return;
11364   }
11365
11366   if (game.restart_level)
11367     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11368
11369   CheckLevelSolved();
11370
11371   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11372     GameWon();
11373
11374   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11375     TapeStop();
11376
11377   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11378     return;
11379
11380   game_frame_delay_value =
11381     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11382
11383   if (tape.playing && tape.warp_forward && !tape.pausing)
11384     game_frame_delay_value = 0;
11385
11386   SetVideoFrameDelay(game_frame_delay_value);
11387
11388   // (de)activate virtual buttons depending on current game status
11389   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11390   {
11391     if (game.all_players_gone)  // if no players there to be controlled anymore
11392       SetOverlayActive(FALSE);
11393     else if (!tape.playing)     // if game continues after tape stopped playing
11394       SetOverlayActive(TRUE);
11395   }
11396
11397 #if 0
11398 #if 0
11399   // ---------- main game synchronization point ----------
11400
11401   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11402
11403   printf("::: skip == %d\n", skip);
11404
11405 #else
11406   // ---------- main game synchronization point ----------
11407
11408   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11409 #endif
11410 #endif
11411
11412   if (network_playing && !network_player_action_received)
11413   {
11414     // try to get network player actions in time
11415
11416     // last chance to get network player actions without main loop delay
11417     HandleNetworking();
11418
11419     // game was quit by network peer
11420     if (game_status != GAME_MODE_PLAYING)
11421       return;
11422
11423     // check if network player actions still missing and game still running
11424     if (!network_player_action_received && !checkGameEnded())
11425       return;           // failed to get network player actions in time
11426
11427     // do not yet reset "network_player_action_received" (for tape.pausing)
11428   }
11429
11430   if (tape.pausing)
11431     return;
11432
11433   // at this point we know that we really continue executing the game
11434
11435   network_player_action_received = FALSE;
11436
11437   // when playing tape, read previously recorded player input from tape data
11438   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11439
11440   local_player->effective_mouse_action = local_player->mouse_action;
11441
11442   if (recorded_player_action != NULL)
11443     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11444                                  recorded_player_action);
11445
11446   // TapePlayAction() may return NULL when toggling to "pause before death"
11447   if (tape.pausing)
11448     return;
11449
11450   if (tape.set_centered_player)
11451   {
11452     game.centered_player_nr_next = tape.centered_player_nr_next;
11453     game.set_centered_player = TRUE;
11454   }
11455
11456   for (i = 0; i < MAX_PLAYERS; i++)
11457   {
11458     summarized_player_action |= stored_player[i].action;
11459
11460     if (!network_playing && (game.team_mode || tape.playing))
11461       stored_player[i].effective_action = stored_player[i].action;
11462   }
11463
11464   if (network_playing && !checkGameEnded())
11465     SendToServer_MovePlayer(summarized_player_action);
11466
11467   // summarize all actions at local players mapped input device position
11468   // (this allows using different input devices in single player mode)
11469   if (!network.enabled && !game.team_mode)
11470     stored_player[map_player_action[local_player->index_nr]].effective_action =
11471       summarized_player_action;
11472
11473   // summarize all actions at centered player in local team mode
11474   if (tape.recording &&
11475       setup.team_mode && !network.enabled &&
11476       setup.input_on_focus &&
11477       game.centered_player_nr != -1)
11478   {
11479     for (i = 0; i < MAX_PLAYERS; i++)
11480       stored_player[map_player_action[i]].effective_action =
11481         (i == game.centered_player_nr ? summarized_player_action : 0);
11482   }
11483
11484   if (recorded_player_action != NULL)
11485     for (i = 0; i < MAX_PLAYERS; i++)
11486       stored_player[i].effective_action = recorded_player_action[i];
11487
11488   for (i = 0; i < MAX_PLAYERS; i++)
11489   {
11490     tape_action[i] = stored_player[i].effective_action;
11491
11492     /* (this may happen in the RND game engine if a player was not present on
11493        the playfield on level start, but appeared later from a custom element */
11494     if (setup.team_mode &&
11495         tape.recording &&
11496         tape_action[i] &&
11497         !tape.player_participates[i])
11498       tape.player_participates[i] = TRUE;
11499   }
11500
11501   SetTapeActionFromMouseAction(tape_action,
11502                                &local_player->effective_mouse_action);
11503
11504   // only record actions from input devices, but not programmed actions
11505   if (tape.recording)
11506     TapeRecordAction(tape_action);
11507
11508   // remember if game was played (especially after tape stopped playing)
11509   if (!tape.playing && summarized_player_action)
11510     game.GamePlayed = TRUE;
11511
11512 #if USE_NEW_PLAYER_ASSIGNMENTS
11513   // !!! also map player actions in single player mode !!!
11514   // if (game.team_mode)
11515   if (1)
11516   {
11517     byte mapped_action[MAX_PLAYERS];
11518
11519 #if DEBUG_PLAYER_ACTIONS
11520     printf(":::");
11521     for (i = 0; i < MAX_PLAYERS; i++)
11522       printf(" %d, ", stored_player[i].effective_action);
11523 #endif
11524
11525     for (i = 0; i < MAX_PLAYERS; i++)
11526       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11527
11528     for (i = 0; i < MAX_PLAYERS; i++)
11529       stored_player[i].effective_action = mapped_action[i];
11530
11531 #if DEBUG_PLAYER_ACTIONS
11532     printf(" =>");
11533     for (i = 0; i < MAX_PLAYERS; i++)
11534       printf(" %d, ", stored_player[i].effective_action);
11535     printf("\n");
11536 #endif
11537   }
11538 #if DEBUG_PLAYER_ACTIONS
11539   else
11540   {
11541     printf(":::");
11542     for (i = 0; i < MAX_PLAYERS; i++)
11543       printf(" %d, ", stored_player[i].effective_action);
11544     printf("\n");
11545   }
11546 #endif
11547 #endif
11548
11549   for (i = 0; i < MAX_PLAYERS; i++)
11550   {
11551     // allow engine snapshot in case of changed movement attempt
11552     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11553         (stored_player[i].effective_action & KEY_MOTION))
11554       game.snapshot.changed_action = TRUE;
11555
11556     // allow engine snapshot in case of snapping/dropping attempt
11557     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11558         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11559       game.snapshot.changed_action = TRUE;
11560
11561     game.snapshot.last_action[i] = stored_player[i].effective_action;
11562   }
11563
11564   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11565   {
11566     GameActions_EM_Main();
11567   }
11568   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11569   {
11570     GameActions_SP_Main();
11571   }
11572   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11573   {
11574     GameActions_MM_Main();
11575   }
11576   else
11577   {
11578     GameActions_RND_Main();
11579   }
11580
11581   BlitScreenToBitmap(backbuffer);
11582
11583   CheckLevelSolved();
11584   CheckLevelTime();
11585
11586   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11587
11588   if (global.show_frames_per_second)
11589   {
11590     static unsigned int fps_counter = 0;
11591     static int fps_frames = 0;
11592     unsigned int fps_delay_ms = Counter() - fps_counter;
11593
11594     fps_frames++;
11595
11596     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11597     {
11598       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11599
11600       fps_frames = 0;
11601       fps_counter = Counter();
11602
11603       // always draw FPS to screen after FPS value was updated
11604       redraw_mask |= REDRAW_FPS;
11605     }
11606
11607     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11608     if (GetDrawDeactivationMask() == REDRAW_NONE)
11609       redraw_mask |= REDRAW_FPS;
11610   }
11611 }
11612
11613 static void GameActions_CheckSaveEngineSnapshot(void)
11614 {
11615   if (!game.snapshot.save_snapshot)
11616     return;
11617
11618   // clear flag for saving snapshot _before_ saving snapshot
11619   game.snapshot.save_snapshot = FALSE;
11620
11621   SaveEngineSnapshotToList();
11622 }
11623
11624 void GameActions(void)
11625 {
11626   GameActionsExt();
11627
11628   GameActions_CheckSaveEngineSnapshot();
11629 }
11630
11631 void GameActions_EM_Main(void)
11632 {
11633   byte effective_action[MAX_PLAYERS];
11634   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11635   int i;
11636
11637   for (i = 0; i < MAX_PLAYERS; i++)
11638     effective_action[i] = stored_player[i].effective_action;
11639
11640   GameActions_EM(effective_action, warp_mode);
11641 }
11642
11643 void GameActions_SP_Main(void)
11644 {
11645   byte effective_action[MAX_PLAYERS];
11646   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11647   int i;
11648
11649   for (i = 0; i < MAX_PLAYERS; i++)
11650     effective_action[i] = stored_player[i].effective_action;
11651
11652   GameActions_SP(effective_action, warp_mode);
11653
11654   for (i = 0; i < MAX_PLAYERS; i++)
11655   {
11656     if (stored_player[i].force_dropping)
11657       stored_player[i].action |= KEY_BUTTON_DROP;
11658
11659     stored_player[i].force_dropping = FALSE;
11660   }
11661 }
11662
11663 void GameActions_MM_Main(void)
11664 {
11665   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11666
11667   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11668 }
11669
11670 void GameActions_RND_Main(void)
11671 {
11672   GameActions_RND();
11673 }
11674
11675 void GameActions_RND(void)
11676 {
11677   int magic_wall_x = 0, magic_wall_y = 0;
11678   int i, x, y, element, graphic, last_gfx_frame;
11679
11680   InitPlayfieldScanModeVars();
11681
11682   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11683   {
11684     SCAN_PLAYFIELD(x, y)
11685     {
11686       ChangeCount[x][y] = 0;
11687       ChangeEvent[x][y] = -1;
11688     }
11689   }
11690
11691   if (game.set_centered_player)
11692   {
11693     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11694
11695     // switching to "all players" only possible if all players fit to screen
11696     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11697     {
11698       game.centered_player_nr_next = game.centered_player_nr;
11699       game.set_centered_player = FALSE;
11700     }
11701
11702     // do not switch focus to non-existing (or non-active) player
11703     if (game.centered_player_nr_next >= 0 &&
11704         !stored_player[game.centered_player_nr_next].active)
11705     {
11706       game.centered_player_nr_next = game.centered_player_nr;
11707       game.set_centered_player = FALSE;
11708     }
11709   }
11710
11711   if (game.set_centered_player &&
11712       ScreenMovPos == 0)        // screen currently aligned at tile position
11713   {
11714     int sx, sy;
11715
11716     if (game.centered_player_nr_next == -1)
11717     {
11718       setScreenCenteredToAllPlayers(&sx, &sy);
11719     }
11720     else
11721     {
11722       sx = stored_player[game.centered_player_nr_next].jx;
11723       sy = stored_player[game.centered_player_nr_next].jy;
11724     }
11725
11726     game.centered_player_nr = game.centered_player_nr_next;
11727     game.set_centered_player = FALSE;
11728
11729     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11730     DrawGameDoorValues();
11731   }
11732
11733   for (i = 0; i < MAX_PLAYERS; i++)
11734   {
11735     int actual_player_action = stored_player[i].effective_action;
11736
11737 #if 1
11738     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11739        - rnd_equinox_tetrachloride 048
11740        - rnd_equinox_tetrachloride_ii 096
11741        - rnd_emanuel_schmieg 002
11742        - doctor_sloan_ww 001, 020
11743     */
11744     if (stored_player[i].MovPos == 0)
11745       CheckGravityMovement(&stored_player[i]);
11746 #endif
11747
11748     // overwrite programmed action with tape action
11749     if (stored_player[i].programmed_action)
11750       actual_player_action = stored_player[i].programmed_action;
11751
11752     PlayerActions(&stored_player[i], actual_player_action);
11753
11754     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11755   }
11756
11757   ScrollScreen(NULL, SCROLL_GO_ON);
11758
11759   /* for backwards compatibility, the following code emulates a fixed bug that
11760      occured when pushing elements (causing elements that just made their last
11761      pushing step to already (if possible) make their first falling step in the
11762      same game frame, which is bad); this code is also needed to use the famous
11763      "spring push bug" which is used in older levels and might be wanted to be
11764      used also in newer levels, but in this case the buggy pushing code is only
11765      affecting the "spring" element and no other elements */
11766
11767   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11768   {
11769     for (i = 0; i < MAX_PLAYERS; i++)
11770     {
11771       struct PlayerInfo *player = &stored_player[i];
11772       int x = player->jx;
11773       int y = player->jy;
11774
11775       if (player->active && player->is_pushing && player->is_moving &&
11776           IS_MOVING(x, y) &&
11777           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11778            Feld[x][y] == EL_SPRING))
11779       {
11780         ContinueMoving(x, y);
11781
11782         // continue moving after pushing (this is actually a bug)
11783         if (!IS_MOVING(x, y))
11784           Stop[x][y] = FALSE;
11785       }
11786     }
11787   }
11788
11789   SCAN_PLAYFIELD(x, y)
11790   {
11791     Last[x][y] = Feld[x][y];
11792
11793     ChangeCount[x][y] = 0;
11794     ChangeEvent[x][y] = -1;
11795
11796     // this must be handled before main playfield loop
11797     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11798     {
11799       MovDelay[x][y]--;
11800       if (MovDelay[x][y] <= 0)
11801         RemoveField(x, y);
11802     }
11803
11804     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11805     {
11806       MovDelay[x][y]--;
11807       if (MovDelay[x][y] <= 0)
11808       {
11809         RemoveField(x, y);
11810         TEST_DrawLevelField(x, y);
11811
11812         TestIfElementTouchesCustomElement(x, y);        // for empty space
11813       }
11814     }
11815
11816 #if DEBUG
11817     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11818     {
11819       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11820       printf("GameActions(): This should never happen!\n");
11821
11822       ChangePage[x][y] = -1;
11823     }
11824 #endif
11825
11826     Stop[x][y] = FALSE;
11827     if (WasJustMoving[x][y] > 0)
11828       WasJustMoving[x][y]--;
11829     if (WasJustFalling[x][y] > 0)
11830       WasJustFalling[x][y]--;
11831     if (CheckCollision[x][y] > 0)
11832       CheckCollision[x][y]--;
11833     if (CheckImpact[x][y] > 0)
11834       CheckImpact[x][y]--;
11835
11836     GfxFrame[x][y]++;
11837
11838     /* reset finished pushing action (not done in ContinueMoving() to allow
11839        continuous pushing animation for elements with zero push delay) */
11840     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11841     {
11842       ResetGfxAnimation(x, y);
11843       TEST_DrawLevelField(x, y);
11844     }
11845
11846 #if DEBUG
11847     if (IS_BLOCKED(x, y))
11848     {
11849       int oldx, oldy;
11850
11851       Blocked2Moving(x, y, &oldx, &oldy);
11852       if (!IS_MOVING(oldx, oldy))
11853       {
11854         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11855         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11856         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11857         printf("GameActions(): This should never happen!\n");
11858       }
11859     }
11860 #endif
11861   }
11862
11863   SCAN_PLAYFIELD(x, y)
11864   {
11865     element = Feld[x][y];
11866     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11867     last_gfx_frame = GfxFrame[x][y];
11868
11869     ResetGfxFrame(x, y);
11870
11871     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11872       DrawLevelGraphicAnimation(x, y, graphic);
11873
11874     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11875         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11876       ResetRandomAnimationValue(x, y);
11877
11878     SetRandomAnimationValue(x, y);
11879
11880     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11881
11882     if (IS_INACTIVE(element))
11883     {
11884       if (IS_ANIMATED(graphic))
11885         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11886
11887       continue;
11888     }
11889
11890     // this may take place after moving, so 'element' may have changed
11891     if (IS_CHANGING(x, y) &&
11892         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11893     {
11894       int page = element_info[element].event_page_nr[CE_DELAY];
11895
11896       HandleElementChange(x, y, page);
11897
11898       element = Feld[x][y];
11899       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11900     }
11901
11902     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11903     {
11904       StartMoving(x, y);
11905
11906       element = Feld[x][y];
11907       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11908
11909       if (IS_ANIMATED(graphic) &&
11910           !IS_MOVING(x, y) &&
11911           !Stop[x][y])
11912         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11913
11914       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11915         TEST_DrawTwinkleOnField(x, y);
11916     }
11917     else if (element == EL_ACID)
11918     {
11919       if (!Stop[x][y])
11920         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11921     }
11922     else if ((element == EL_EXIT_OPEN ||
11923               element == EL_EM_EXIT_OPEN ||
11924               element == EL_SP_EXIT_OPEN ||
11925               element == EL_STEEL_EXIT_OPEN ||
11926               element == EL_EM_STEEL_EXIT_OPEN ||
11927               element == EL_SP_TERMINAL ||
11928               element == EL_SP_TERMINAL_ACTIVE ||
11929               element == EL_EXTRA_TIME ||
11930               element == EL_SHIELD_NORMAL ||
11931               element == EL_SHIELD_DEADLY) &&
11932              IS_ANIMATED(graphic))
11933       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11934     else if (IS_MOVING(x, y))
11935       ContinueMoving(x, y);
11936     else if (IS_ACTIVE_BOMB(element))
11937       CheckDynamite(x, y);
11938     else if (element == EL_AMOEBA_GROWING)
11939       AmoebeWaechst(x, y);
11940     else if (element == EL_AMOEBA_SHRINKING)
11941       AmoebaDisappearing(x, y);
11942
11943 #if !USE_NEW_AMOEBA_CODE
11944     else if (IS_AMOEBALIVE(element))
11945       AmoebeAbleger(x, y);
11946 #endif
11947
11948     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11949       Life(x, y);
11950     else if (element == EL_EXIT_CLOSED)
11951       CheckExit(x, y);
11952     else if (element == EL_EM_EXIT_CLOSED)
11953       CheckExitEM(x, y);
11954     else if (element == EL_STEEL_EXIT_CLOSED)
11955       CheckExitSteel(x, y);
11956     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11957       CheckExitSteelEM(x, y);
11958     else if (element == EL_SP_EXIT_CLOSED)
11959       CheckExitSP(x, y);
11960     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11961              element == EL_EXPANDABLE_STEELWALL_GROWING)
11962       MauerWaechst(x, y);
11963     else if (element == EL_EXPANDABLE_WALL ||
11964              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11965              element == EL_EXPANDABLE_WALL_VERTICAL ||
11966              element == EL_EXPANDABLE_WALL_ANY ||
11967              element == EL_BD_EXPANDABLE_WALL)
11968       MauerAbleger(x, y);
11969     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11970              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11971              element == EL_EXPANDABLE_STEELWALL_ANY)
11972       MauerAblegerStahl(x, y);
11973     else if (element == EL_FLAMES)
11974       CheckForDragon(x, y);
11975     else if (element == EL_EXPLOSION)
11976       ; // drawing of correct explosion animation is handled separately
11977     else if (element == EL_ELEMENT_SNAPPING ||
11978              element == EL_DIAGONAL_SHRINKING ||
11979              element == EL_DIAGONAL_GROWING)
11980     {
11981       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11982
11983       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11984     }
11985     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11986       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11987
11988     if (IS_BELT_ACTIVE(element))
11989       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11990
11991     if (game.magic_wall_active)
11992     {
11993       int jx = local_player->jx, jy = local_player->jy;
11994
11995       // play the element sound at the position nearest to the player
11996       if ((element == EL_MAGIC_WALL_FULL ||
11997            element == EL_MAGIC_WALL_ACTIVE ||
11998            element == EL_MAGIC_WALL_EMPTYING ||
11999            element == EL_BD_MAGIC_WALL_FULL ||
12000            element == EL_BD_MAGIC_WALL_ACTIVE ||
12001            element == EL_BD_MAGIC_WALL_EMPTYING ||
12002            element == EL_DC_MAGIC_WALL_FULL ||
12003            element == EL_DC_MAGIC_WALL_ACTIVE ||
12004            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12005           ABS(x - jx) + ABS(y - jy) <
12006           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12007       {
12008         magic_wall_x = x;
12009         magic_wall_y = y;
12010       }
12011     }
12012   }
12013
12014 #if USE_NEW_AMOEBA_CODE
12015   // new experimental amoeba growth stuff
12016   if (!(FrameCounter % 8))
12017   {
12018     static unsigned int random = 1684108901;
12019
12020     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12021     {
12022       x = RND(lev_fieldx);
12023       y = RND(lev_fieldy);
12024       element = Feld[x][y];
12025
12026       if (!IS_PLAYER(x,y) &&
12027           (element == EL_EMPTY ||
12028            CAN_GROW_INTO(element) ||
12029            element == EL_QUICKSAND_EMPTY ||
12030            element == EL_QUICKSAND_FAST_EMPTY ||
12031            element == EL_ACID_SPLASH_LEFT ||
12032            element == EL_ACID_SPLASH_RIGHT))
12033       {
12034         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12035             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12036             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12037             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12038           Feld[x][y] = EL_AMOEBA_DROP;
12039       }
12040
12041       random = random * 129 + 1;
12042     }
12043   }
12044 #endif
12045
12046   game.explosions_delayed = FALSE;
12047
12048   SCAN_PLAYFIELD(x, y)
12049   {
12050     element = Feld[x][y];
12051
12052     if (ExplodeField[x][y])
12053       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12054     else if (element == EL_EXPLOSION)
12055       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12056
12057     ExplodeField[x][y] = EX_TYPE_NONE;
12058   }
12059
12060   game.explosions_delayed = TRUE;
12061
12062   if (game.magic_wall_active)
12063   {
12064     if (!(game.magic_wall_time_left % 4))
12065     {
12066       int element = Feld[magic_wall_x][magic_wall_y];
12067
12068       if (element == EL_BD_MAGIC_WALL_FULL ||
12069           element == EL_BD_MAGIC_WALL_ACTIVE ||
12070           element == EL_BD_MAGIC_WALL_EMPTYING)
12071         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12072       else if (element == EL_DC_MAGIC_WALL_FULL ||
12073                element == EL_DC_MAGIC_WALL_ACTIVE ||
12074                element == EL_DC_MAGIC_WALL_EMPTYING)
12075         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12076       else
12077         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12078     }
12079
12080     if (game.magic_wall_time_left > 0)
12081     {
12082       game.magic_wall_time_left--;
12083
12084       if (!game.magic_wall_time_left)
12085       {
12086         SCAN_PLAYFIELD(x, y)
12087         {
12088           element = Feld[x][y];
12089
12090           if (element == EL_MAGIC_WALL_ACTIVE ||
12091               element == EL_MAGIC_WALL_FULL)
12092           {
12093             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12094             TEST_DrawLevelField(x, y);
12095           }
12096           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12097                    element == EL_BD_MAGIC_WALL_FULL)
12098           {
12099             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12100             TEST_DrawLevelField(x, y);
12101           }
12102           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12103                    element == EL_DC_MAGIC_WALL_FULL)
12104           {
12105             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12106             TEST_DrawLevelField(x, y);
12107           }
12108         }
12109
12110         game.magic_wall_active = FALSE;
12111       }
12112     }
12113   }
12114
12115   if (game.light_time_left > 0)
12116   {
12117     game.light_time_left--;
12118
12119     if (game.light_time_left == 0)
12120       RedrawAllLightSwitchesAndInvisibleElements();
12121   }
12122
12123   if (game.timegate_time_left > 0)
12124   {
12125     game.timegate_time_left--;
12126
12127     if (game.timegate_time_left == 0)
12128       CloseAllOpenTimegates();
12129   }
12130
12131   if (game.lenses_time_left > 0)
12132   {
12133     game.lenses_time_left--;
12134
12135     if (game.lenses_time_left == 0)
12136       RedrawAllInvisibleElementsForLenses();
12137   }
12138
12139   if (game.magnify_time_left > 0)
12140   {
12141     game.magnify_time_left--;
12142
12143     if (game.magnify_time_left == 0)
12144       RedrawAllInvisibleElementsForMagnifier();
12145   }
12146
12147   for (i = 0; i < MAX_PLAYERS; i++)
12148   {
12149     struct PlayerInfo *player = &stored_player[i];
12150
12151     if (SHIELD_ON(player))
12152     {
12153       if (player->shield_deadly_time_left)
12154         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12155       else if (player->shield_normal_time_left)
12156         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12157     }
12158   }
12159
12160 #if USE_DELAYED_GFX_REDRAW
12161   SCAN_PLAYFIELD(x, y)
12162   {
12163     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12164     {
12165       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12166          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12167
12168       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12169         DrawLevelField(x, y);
12170
12171       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12172         DrawLevelFieldCrumbled(x, y);
12173
12174       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12175         DrawLevelFieldCrumbledNeighbours(x, y);
12176
12177       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12178         DrawTwinkleOnField(x, y);
12179     }
12180
12181     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12182   }
12183 #endif
12184
12185   DrawAllPlayers();
12186   PlayAllPlayersSound();
12187
12188   for (i = 0; i < MAX_PLAYERS; i++)
12189   {
12190     struct PlayerInfo *player = &stored_player[i];
12191
12192     if (player->show_envelope != 0 && (!player->active ||
12193                                        player->MovPos == 0))
12194     {
12195       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12196
12197       player->show_envelope = 0;
12198     }
12199   }
12200
12201   // use random number generator in every frame to make it less predictable
12202   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12203     RND(1);
12204 }
12205
12206 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12207 {
12208   int min_x = x, min_y = y, max_x = x, max_y = y;
12209   int i;
12210
12211   for (i = 0; i < MAX_PLAYERS; i++)
12212   {
12213     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12214
12215     if (!stored_player[i].active || &stored_player[i] == player)
12216       continue;
12217
12218     min_x = MIN(min_x, jx);
12219     min_y = MIN(min_y, jy);
12220     max_x = MAX(max_x, jx);
12221     max_y = MAX(max_y, jy);
12222   }
12223
12224   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12225 }
12226
12227 static boolean AllPlayersInVisibleScreen(void)
12228 {
12229   int i;
12230
12231   for (i = 0; i < MAX_PLAYERS; i++)
12232   {
12233     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12234
12235     if (!stored_player[i].active)
12236       continue;
12237
12238     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12239       return FALSE;
12240   }
12241
12242   return TRUE;
12243 }
12244
12245 void ScrollLevel(int dx, int dy)
12246 {
12247   int scroll_offset = 2 * TILEX_VAR;
12248   int x, y;
12249
12250   BlitBitmap(drawto_field, drawto_field,
12251              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12252              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12253              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12254              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12255              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12256              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12257
12258   if (dx != 0)
12259   {
12260     x = (dx == 1 ? BX1 : BX2);
12261     for (y = BY1; y <= BY2; y++)
12262       DrawScreenField(x, y);
12263   }
12264
12265   if (dy != 0)
12266   {
12267     y = (dy == 1 ? BY1 : BY2);
12268     for (x = BX1; x <= BX2; x++)
12269       DrawScreenField(x, y);
12270   }
12271
12272   redraw_mask |= REDRAW_FIELD;
12273 }
12274
12275 static boolean canFallDown(struct PlayerInfo *player)
12276 {
12277   int jx = player->jx, jy = player->jy;
12278
12279   return (IN_LEV_FIELD(jx, jy + 1) &&
12280           (IS_FREE(jx, jy + 1) ||
12281            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12282           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12283           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12284 }
12285
12286 static boolean canPassField(int x, int y, int move_dir)
12287 {
12288   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12289   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12290   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12291   int nextx = x + dx;
12292   int nexty = y + dy;
12293   int element = Feld[x][y];
12294
12295   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12296           !CAN_MOVE(element) &&
12297           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12298           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12299           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12300 }
12301
12302 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12303 {
12304   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12305   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12306   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12307   int newx = x + dx;
12308   int newy = y + dy;
12309
12310   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12311           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12312           (IS_DIGGABLE(Feld[newx][newy]) ||
12313            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12314            canPassField(newx, newy, move_dir)));
12315 }
12316
12317 static void CheckGravityMovement(struct PlayerInfo *player)
12318 {
12319   if (player->gravity && !player->programmed_action)
12320   {
12321     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12322     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12323     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12324     int jx = player->jx, jy = player->jy;
12325     boolean player_is_moving_to_valid_field =
12326       (!player_is_snapping &&
12327        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12328         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12329     boolean player_can_fall_down = canFallDown(player);
12330
12331     if (player_can_fall_down &&
12332         !player_is_moving_to_valid_field)
12333       player->programmed_action = MV_DOWN;
12334   }
12335 }
12336
12337 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12338 {
12339   return CheckGravityMovement(player);
12340
12341   if (player->gravity && !player->programmed_action)
12342   {
12343     int jx = player->jx, jy = player->jy;
12344     boolean field_under_player_is_free =
12345       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12346     boolean player_is_standing_on_valid_field =
12347       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12348        (IS_WALKABLE(Feld[jx][jy]) &&
12349         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12350
12351     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12352       player->programmed_action = MV_DOWN;
12353   }
12354 }
12355
12356 /*
12357   MovePlayerOneStep()
12358   -----------------------------------------------------------------------------
12359   dx, dy:               direction (non-diagonal) to try to move the player to
12360   real_dx, real_dy:     direction as read from input device (can be diagonal)
12361 */
12362
12363 boolean MovePlayerOneStep(struct PlayerInfo *player,
12364                           int dx, int dy, int real_dx, int real_dy)
12365 {
12366   int jx = player->jx, jy = player->jy;
12367   int new_jx = jx + dx, new_jy = jy + dy;
12368   int can_move;
12369   boolean player_can_move = !player->cannot_move;
12370
12371   if (!player->active || (!dx && !dy))
12372     return MP_NO_ACTION;
12373
12374   player->MovDir = (dx < 0 ? MV_LEFT :
12375                     dx > 0 ? MV_RIGHT :
12376                     dy < 0 ? MV_UP :
12377                     dy > 0 ? MV_DOWN :  MV_NONE);
12378
12379   if (!IN_LEV_FIELD(new_jx, new_jy))
12380     return MP_NO_ACTION;
12381
12382   if (!player_can_move)
12383   {
12384     if (player->MovPos == 0)
12385     {
12386       player->is_moving = FALSE;
12387       player->is_digging = FALSE;
12388       player->is_collecting = FALSE;
12389       player->is_snapping = FALSE;
12390       player->is_pushing = FALSE;
12391     }
12392   }
12393
12394   if (!network.enabled && game.centered_player_nr == -1 &&
12395       !AllPlayersInSight(player, new_jx, new_jy))
12396     return MP_NO_ACTION;
12397
12398   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12399   if (can_move != MP_MOVING)
12400     return can_move;
12401
12402   // check if DigField() has caused relocation of the player
12403   if (player->jx != jx || player->jy != jy)
12404     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12405
12406   StorePlayer[jx][jy] = 0;
12407   player->last_jx = jx;
12408   player->last_jy = jy;
12409   player->jx = new_jx;
12410   player->jy = new_jy;
12411   StorePlayer[new_jx][new_jy] = player->element_nr;
12412
12413   if (player->move_delay_value_next != -1)
12414   {
12415     player->move_delay_value = player->move_delay_value_next;
12416     player->move_delay_value_next = -1;
12417   }
12418
12419   player->MovPos =
12420     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12421
12422   player->step_counter++;
12423
12424   PlayerVisit[jx][jy] = FrameCounter;
12425
12426   player->is_moving = TRUE;
12427
12428 #if 1
12429   // should better be called in MovePlayer(), but this breaks some tapes
12430   ScrollPlayer(player, SCROLL_INIT);
12431 #endif
12432
12433   return MP_MOVING;
12434 }
12435
12436 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12437 {
12438   int jx = player->jx, jy = player->jy;
12439   int old_jx = jx, old_jy = jy;
12440   int moved = MP_NO_ACTION;
12441
12442   if (!player->active)
12443     return FALSE;
12444
12445   if (!dx && !dy)
12446   {
12447     if (player->MovPos == 0)
12448     {
12449       player->is_moving = FALSE;
12450       player->is_digging = FALSE;
12451       player->is_collecting = FALSE;
12452       player->is_snapping = FALSE;
12453       player->is_pushing = FALSE;
12454     }
12455
12456     return FALSE;
12457   }
12458
12459   if (player->move_delay > 0)
12460     return FALSE;
12461
12462   player->move_delay = -1;              // set to "uninitialized" value
12463
12464   // store if player is automatically moved to next field
12465   player->is_auto_moving = (player->programmed_action != MV_NONE);
12466
12467   // remove the last programmed player action
12468   player->programmed_action = 0;
12469
12470   if (player->MovPos)
12471   {
12472     // should only happen if pre-1.2 tape recordings are played
12473     // this is only for backward compatibility
12474
12475     int original_move_delay_value = player->move_delay_value;
12476
12477 #if DEBUG
12478     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12479            tape.counter);
12480 #endif
12481
12482     // scroll remaining steps with finest movement resolution
12483     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12484
12485     while (player->MovPos)
12486     {
12487       ScrollPlayer(player, SCROLL_GO_ON);
12488       ScrollScreen(NULL, SCROLL_GO_ON);
12489
12490       AdvanceFrameAndPlayerCounters(player->index_nr);
12491
12492       DrawAllPlayers();
12493       BackToFront_WithFrameDelay(0);
12494     }
12495
12496     player->move_delay_value = original_move_delay_value;
12497   }
12498
12499   player->is_active = FALSE;
12500
12501   if (player->last_move_dir & MV_HORIZONTAL)
12502   {
12503     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12504       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12505   }
12506   else
12507   {
12508     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12509       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12510   }
12511
12512   if (!moved && !player->is_active)
12513   {
12514     player->is_moving = FALSE;
12515     player->is_digging = FALSE;
12516     player->is_collecting = FALSE;
12517     player->is_snapping = FALSE;
12518     player->is_pushing = FALSE;
12519   }
12520
12521   jx = player->jx;
12522   jy = player->jy;
12523
12524   if (moved & MP_MOVING && !ScreenMovPos &&
12525       (player->index_nr == game.centered_player_nr ||
12526        game.centered_player_nr == -1))
12527   {
12528     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12529
12530     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12531     {
12532       // actual player has left the screen -- scroll in that direction
12533       if (jx != old_jx)         // player has moved horizontally
12534         scroll_x += (jx - old_jx);
12535       else                      // player has moved vertically
12536         scroll_y += (jy - old_jy);
12537     }
12538     else
12539     {
12540       int offset_raw = game.scroll_delay_value;
12541
12542       if (jx != old_jx)         // player has moved horizontally
12543       {
12544         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12545         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12546         int new_scroll_x = jx - MIDPOSX + offset_x;
12547
12548         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12549             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12550           scroll_x = new_scroll_x;
12551
12552         // don't scroll over playfield boundaries
12553         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12554
12555         // don't scroll more than one field at a time
12556         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12557
12558         // don't scroll against the player's moving direction
12559         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12560             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12561           scroll_x = old_scroll_x;
12562       }
12563       else                      // player has moved vertically
12564       {
12565         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12566         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12567         int new_scroll_y = jy - MIDPOSY + offset_y;
12568
12569         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12570             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12571           scroll_y = new_scroll_y;
12572
12573         // don't scroll over playfield boundaries
12574         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12575
12576         // don't scroll more than one field at a time
12577         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12578
12579         // don't scroll against the player's moving direction
12580         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12581             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12582           scroll_y = old_scroll_y;
12583       }
12584     }
12585
12586     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12587     {
12588       if (!network.enabled && game.centered_player_nr == -1 &&
12589           !AllPlayersInVisibleScreen())
12590       {
12591         scroll_x = old_scroll_x;
12592         scroll_y = old_scroll_y;
12593       }
12594       else
12595       {
12596         ScrollScreen(player, SCROLL_INIT);
12597         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12598       }
12599     }
12600   }
12601
12602   player->StepFrame = 0;
12603
12604   if (moved & MP_MOVING)
12605   {
12606     if (old_jx != jx && old_jy == jy)
12607       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12608     else if (old_jx == jx && old_jy != jy)
12609       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12610
12611     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12612
12613     player->last_move_dir = player->MovDir;
12614     player->is_moving = TRUE;
12615     player->is_snapping = FALSE;
12616     player->is_switching = FALSE;
12617     player->is_dropping = FALSE;
12618     player->is_dropping_pressed = FALSE;
12619     player->drop_pressed_delay = 0;
12620
12621 #if 0
12622     // should better be called here than above, but this breaks some tapes
12623     ScrollPlayer(player, SCROLL_INIT);
12624 #endif
12625   }
12626   else
12627   {
12628     CheckGravityMovementWhenNotMoving(player);
12629
12630     player->is_moving = FALSE;
12631
12632     /* at this point, the player is allowed to move, but cannot move right now
12633        (e.g. because of something blocking the way) -- ensure that the player
12634        is also allowed to move in the next frame (in old versions before 3.1.1,
12635        the player was forced to wait again for eight frames before next try) */
12636
12637     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12638       player->move_delay = 0;   // allow direct movement in the next frame
12639   }
12640
12641   if (player->move_delay == -1)         // not yet initialized by DigField()
12642     player->move_delay = player->move_delay_value;
12643
12644   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12645   {
12646     TestIfPlayerTouchesBadThing(jx, jy);
12647     TestIfPlayerTouchesCustomElement(jx, jy);
12648   }
12649
12650   if (!player->active)
12651     RemovePlayer(player);
12652
12653   return moved;
12654 }
12655
12656 void ScrollPlayer(struct PlayerInfo *player, int mode)
12657 {
12658   int jx = player->jx, jy = player->jy;
12659   int last_jx = player->last_jx, last_jy = player->last_jy;
12660   int move_stepsize = TILEX / player->move_delay_value;
12661
12662   if (!player->active)
12663     return;
12664
12665   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12666     return;
12667
12668   if (mode == SCROLL_INIT)
12669   {
12670     player->actual_frame_counter = FrameCounter;
12671     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12672
12673     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12674         Feld[last_jx][last_jy] == EL_EMPTY)
12675     {
12676       int last_field_block_delay = 0;   // start with no blocking at all
12677       int block_delay_adjustment = player->block_delay_adjustment;
12678
12679       // if player blocks last field, add delay for exactly one move
12680       if (player->block_last_field)
12681       {
12682         last_field_block_delay += player->move_delay_value;
12683
12684         // when blocking enabled, prevent moving up despite gravity
12685         if (player->gravity && player->MovDir == MV_UP)
12686           block_delay_adjustment = -1;
12687       }
12688
12689       // add block delay adjustment (also possible when not blocking)
12690       last_field_block_delay += block_delay_adjustment;
12691
12692       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12693       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12694     }
12695
12696     if (player->MovPos != 0)    // player has not yet reached destination
12697       return;
12698   }
12699   else if (!FrameReached(&player->actual_frame_counter, 1))
12700     return;
12701
12702   if (player->MovPos != 0)
12703   {
12704     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12705     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12706
12707     // before DrawPlayer() to draw correct player graphic for this case
12708     if (player->MovPos == 0)
12709       CheckGravityMovement(player);
12710   }
12711
12712   if (player->MovPos == 0)      // player reached destination field
12713   {
12714     if (player->move_delay_reset_counter > 0)
12715     {
12716       player->move_delay_reset_counter--;
12717
12718       if (player->move_delay_reset_counter == 0)
12719       {
12720         // continue with normal speed after quickly moving through gate
12721         HALVE_PLAYER_SPEED(player);
12722
12723         // be able to make the next move without delay
12724         player->move_delay = 0;
12725       }
12726     }
12727
12728     player->last_jx = jx;
12729     player->last_jy = jy;
12730
12731     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12732         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12733         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12734         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12735         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12736         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12737         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12738         Feld[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
12739     {
12740       ExitPlayer(player);
12741
12742       if (game.players_still_needed == 0 &&
12743           (game.friends_still_needed == 0 ||
12744            IS_SP_ELEMENT(Feld[jx][jy])))
12745         LevelSolved();
12746     }
12747
12748     // this breaks one level: "machine", level 000
12749     {
12750       int move_direction = player->MovDir;
12751       int enter_side = MV_DIR_OPPOSITE(move_direction);
12752       int leave_side = move_direction;
12753       int old_jx = last_jx;
12754       int old_jy = last_jy;
12755       int old_element = Feld[old_jx][old_jy];
12756       int new_element = Feld[jx][jy];
12757
12758       if (IS_CUSTOM_ELEMENT(old_element))
12759         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12760                                    CE_LEFT_BY_PLAYER,
12761                                    player->index_bit, leave_side);
12762
12763       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12764                                           CE_PLAYER_LEAVES_X,
12765                                           player->index_bit, leave_side);
12766
12767       if (IS_CUSTOM_ELEMENT(new_element))
12768         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12769                                    player->index_bit, enter_side);
12770
12771       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12772                                           CE_PLAYER_ENTERS_X,
12773                                           player->index_bit, enter_side);
12774
12775       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12776                                         CE_MOVE_OF_X, move_direction);
12777     }
12778
12779     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12780     {
12781       TestIfPlayerTouchesBadThing(jx, jy);
12782       TestIfPlayerTouchesCustomElement(jx, jy);
12783
12784       /* needed because pushed element has not yet reached its destination,
12785          so it would trigger a change event at its previous field location */
12786       if (!player->is_pushing)
12787         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
12788
12789       if (!player->active)
12790         RemovePlayer(player);
12791     }
12792
12793     if (!game.LevelSolved && level.use_step_counter)
12794     {
12795       int i;
12796
12797       TimePlayed++;
12798
12799       if (TimeLeft > 0)
12800       {
12801         TimeLeft--;
12802
12803         if (TimeLeft <= 10 && setup.time_limit)
12804           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12805
12806         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12807
12808         DisplayGameControlValues();
12809
12810         if (!TimeLeft && setup.time_limit)
12811           for (i = 0; i < MAX_PLAYERS; i++)
12812             KillPlayer(&stored_player[i]);
12813       }
12814       else if (game.no_time_limit && !game.all_players_gone)
12815       {
12816         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12817
12818         DisplayGameControlValues();
12819       }
12820     }
12821
12822     if (tape.single_step && tape.recording && !tape.pausing &&
12823         !player->programmed_action)
12824       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12825
12826     if (!player->programmed_action)
12827       CheckSaveEngineSnapshot(player);
12828   }
12829 }
12830
12831 void ScrollScreen(struct PlayerInfo *player, int mode)
12832 {
12833   static unsigned int screen_frame_counter = 0;
12834
12835   if (mode == SCROLL_INIT)
12836   {
12837     // set scrolling step size according to actual player's moving speed
12838     ScrollStepSize = TILEX / player->move_delay_value;
12839
12840     screen_frame_counter = FrameCounter;
12841     ScreenMovDir = player->MovDir;
12842     ScreenMovPos = player->MovPos;
12843     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12844     return;
12845   }
12846   else if (!FrameReached(&screen_frame_counter, 1))
12847     return;
12848
12849   if (ScreenMovPos)
12850   {
12851     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12852     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12853     redraw_mask |= REDRAW_FIELD;
12854   }
12855   else
12856     ScreenMovDir = MV_NONE;
12857 }
12858
12859 void TestIfPlayerTouchesCustomElement(int x, int y)
12860 {
12861   static int xy[4][2] =
12862   {
12863     { 0, -1 },
12864     { -1, 0 },
12865     { +1, 0 },
12866     { 0, +1 }
12867   };
12868   static int trigger_sides[4][2] =
12869   {
12870     // center side       border side
12871     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12872     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12873     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12874     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12875   };
12876   static int touch_dir[4] =
12877   {
12878     MV_LEFT | MV_RIGHT,
12879     MV_UP   | MV_DOWN,
12880     MV_UP   | MV_DOWN,
12881     MV_LEFT | MV_RIGHT
12882   };
12883   int center_element = Feld[x][y];      // should always be non-moving!
12884   int i;
12885
12886   for (i = 0; i < NUM_DIRECTIONS; i++)
12887   {
12888     int xx = x + xy[i][0];
12889     int yy = y + xy[i][1];
12890     int center_side = trigger_sides[i][0];
12891     int border_side = trigger_sides[i][1];
12892     int border_element;
12893
12894     if (!IN_LEV_FIELD(xx, yy))
12895       continue;
12896
12897     if (IS_PLAYER(x, y))                // player found at center element
12898     {
12899       struct PlayerInfo *player = PLAYERINFO(x, y);
12900
12901       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12902         border_element = Feld[xx][yy];          // may be moving!
12903       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12904         border_element = Feld[xx][yy];
12905       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
12906         border_element = MovingOrBlocked2Element(xx, yy);
12907       else
12908         continue;               // center and border element do not touch
12909
12910       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12911                                  player->index_bit, border_side);
12912       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12913                                           CE_PLAYER_TOUCHES_X,
12914                                           player->index_bit, border_side);
12915
12916       {
12917         /* use player element that is initially defined in the level playfield,
12918            not the player element that corresponds to the runtime player number
12919            (example: a level that contains EL_PLAYER_3 as the only player would
12920            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12921         int player_element = PLAYERINFO(x, y)->initial_element;
12922
12923         CheckElementChangeBySide(xx, yy, border_element, player_element,
12924                                  CE_TOUCHING_X, border_side);
12925       }
12926     }
12927     else if (IS_PLAYER(xx, yy))         // player found at border element
12928     {
12929       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12930
12931       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12932       {
12933         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12934           continue;             // center and border element do not touch
12935       }
12936
12937       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12938                                  player->index_bit, center_side);
12939       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12940                                           CE_PLAYER_TOUCHES_X,
12941                                           player->index_bit, center_side);
12942
12943       {
12944         /* use player element that is initially defined in the level playfield,
12945            not the player element that corresponds to the runtime player number
12946            (example: a level that contains EL_PLAYER_3 as the only player would
12947            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12948         int player_element = PLAYERINFO(xx, yy)->initial_element;
12949
12950         CheckElementChangeBySide(x, y, center_element, player_element,
12951                                  CE_TOUCHING_X, center_side);
12952       }
12953
12954       break;
12955     }
12956   }
12957 }
12958
12959 void TestIfElementTouchesCustomElement(int x, int y)
12960 {
12961   static int xy[4][2] =
12962   {
12963     { 0, -1 },
12964     { -1, 0 },
12965     { +1, 0 },
12966     { 0, +1 }
12967   };
12968   static int trigger_sides[4][2] =
12969   {
12970     // center side      border side
12971     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
12972     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
12973     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
12974     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
12975   };
12976   static int touch_dir[4] =
12977   {
12978     MV_LEFT | MV_RIGHT,
12979     MV_UP   | MV_DOWN,
12980     MV_UP   | MV_DOWN,
12981     MV_LEFT | MV_RIGHT
12982   };
12983   boolean change_center_element = FALSE;
12984   int center_element = Feld[x][y];      // should always be non-moving!
12985   int border_element_old[NUM_DIRECTIONS];
12986   int i;
12987
12988   for (i = 0; i < NUM_DIRECTIONS; i++)
12989   {
12990     int xx = x + xy[i][0];
12991     int yy = y + xy[i][1];
12992     int border_element;
12993
12994     border_element_old[i] = -1;
12995
12996     if (!IN_LEV_FIELD(xx, yy))
12997       continue;
12998
12999     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13000       border_element = Feld[xx][yy];    // may be moving!
13001     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13002       border_element = Feld[xx][yy];
13003     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13004       border_element = MovingOrBlocked2Element(xx, yy);
13005     else
13006       continue;                 // center and border element do not touch
13007
13008     border_element_old[i] = border_element;
13009   }
13010
13011   for (i = 0; i < NUM_DIRECTIONS; i++)
13012   {
13013     int xx = x + xy[i][0];
13014     int yy = y + xy[i][1];
13015     int center_side = trigger_sides[i][0];
13016     int border_element = border_element_old[i];
13017
13018     if (border_element == -1)
13019       continue;
13020
13021     // check for change of border element
13022     CheckElementChangeBySide(xx, yy, border_element, center_element,
13023                              CE_TOUCHING_X, center_side);
13024
13025     // (center element cannot be player, so we dont have to check this here)
13026   }
13027
13028   for (i = 0; i < NUM_DIRECTIONS; i++)
13029   {
13030     int xx = x + xy[i][0];
13031     int yy = y + xy[i][1];
13032     int border_side = trigger_sides[i][1];
13033     int border_element = border_element_old[i];
13034
13035     if (border_element == -1)
13036       continue;
13037
13038     // check for change of center element (but change it only once)
13039     if (!change_center_element)
13040       change_center_element =
13041         CheckElementChangeBySide(x, y, center_element, border_element,
13042                                  CE_TOUCHING_X, border_side);
13043
13044     if (IS_PLAYER(xx, yy))
13045     {
13046       /* use player element that is initially defined in the level playfield,
13047          not the player element that corresponds to the runtime player number
13048          (example: a level that contains EL_PLAYER_3 as the only player would
13049          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13050       int player_element = PLAYERINFO(xx, yy)->initial_element;
13051
13052       CheckElementChangeBySide(x, y, center_element, player_element,
13053                                CE_TOUCHING_X, border_side);
13054     }
13055   }
13056 }
13057
13058 void TestIfElementHitsCustomElement(int x, int y, int direction)
13059 {
13060   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13061   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13062   int hitx = x + dx, hity = y + dy;
13063   int hitting_element = Feld[x][y];
13064   int touched_element;
13065
13066   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13067     return;
13068
13069   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13070                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13071
13072   if (IN_LEV_FIELD(hitx, hity))
13073   {
13074     int opposite_direction = MV_DIR_OPPOSITE(direction);
13075     int hitting_side = direction;
13076     int touched_side = opposite_direction;
13077     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13078                           MovDir[hitx][hity] != direction ||
13079                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13080
13081     object_hit = TRUE;
13082
13083     if (object_hit)
13084     {
13085       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13086                                CE_HITTING_X, touched_side);
13087
13088       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13089                                CE_HIT_BY_X, hitting_side);
13090
13091       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13092                                CE_HIT_BY_SOMETHING, opposite_direction);
13093
13094       if (IS_PLAYER(hitx, hity))
13095       {
13096         /* use player element that is initially defined in the level playfield,
13097            not the player element that corresponds to the runtime player number
13098            (example: a level that contains EL_PLAYER_3 as the only player would
13099            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13100         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13101
13102         CheckElementChangeBySide(x, y, hitting_element, player_element,
13103                                  CE_HITTING_X, touched_side);
13104       }
13105     }
13106   }
13107
13108   // "hitting something" is also true when hitting the playfield border
13109   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13110                            CE_HITTING_SOMETHING, direction);
13111 }
13112
13113 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13114 {
13115   int i, kill_x = -1, kill_y = -1;
13116
13117   int bad_element = -1;
13118   static int test_xy[4][2] =
13119   {
13120     { 0, -1 },
13121     { -1, 0 },
13122     { +1, 0 },
13123     { 0, +1 }
13124   };
13125   static int test_dir[4] =
13126   {
13127     MV_UP,
13128     MV_LEFT,
13129     MV_RIGHT,
13130     MV_DOWN
13131   };
13132
13133   for (i = 0; i < NUM_DIRECTIONS; i++)
13134   {
13135     int test_x, test_y, test_move_dir, test_element;
13136
13137     test_x = good_x + test_xy[i][0];
13138     test_y = good_y + test_xy[i][1];
13139
13140     if (!IN_LEV_FIELD(test_x, test_y))
13141       continue;
13142
13143     test_move_dir =
13144       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13145
13146     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13147
13148     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13149        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13150     */
13151     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13152         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13153     {
13154       kill_x = test_x;
13155       kill_y = test_y;
13156       bad_element = test_element;
13157
13158       break;
13159     }
13160   }
13161
13162   if (kill_x != -1 || kill_y != -1)
13163   {
13164     if (IS_PLAYER(good_x, good_y))
13165     {
13166       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13167
13168       if (player->shield_deadly_time_left > 0 &&
13169           !IS_INDESTRUCTIBLE(bad_element))
13170         Bang(kill_x, kill_y);
13171       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13172         KillPlayer(player);
13173     }
13174     else
13175       Bang(good_x, good_y);
13176   }
13177 }
13178
13179 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13180 {
13181   int i, kill_x = -1, kill_y = -1;
13182   int bad_element = Feld[bad_x][bad_y];
13183   static int test_xy[4][2] =
13184   {
13185     { 0, -1 },
13186     { -1, 0 },
13187     { +1, 0 },
13188     { 0, +1 }
13189   };
13190   static int touch_dir[4] =
13191   {
13192     MV_LEFT | MV_RIGHT,
13193     MV_UP   | MV_DOWN,
13194     MV_UP   | MV_DOWN,
13195     MV_LEFT | MV_RIGHT
13196   };
13197   static int test_dir[4] =
13198   {
13199     MV_UP,
13200     MV_LEFT,
13201     MV_RIGHT,
13202     MV_DOWN
13203   };
13204
13205   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13206     return;
13207
13208   for (i = 0; i < NUM_DIRECTIONS; i++)
13209   {
13210     int test_x, test_y, test_move_dir, test_element;
13211
13212     test_x = bad_x + test_xy[i][0];
13213     test_y = bad_y + test_xy[i][1];
13214
13215     if (!IN_LEV_FIELD(test_x, test_y))
13216       continue;
13217
13218     test_move_dir =
13219       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13220
13221     test_element = Feld[test_x][test_y];
13222
13223     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13224        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13225     */
13226     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13227         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13228     {
13229       // good thing is player or penguin that does not move away
13230       if (IS_PLAYER(test_x, test_y))
13231       {
13232         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13233
13234         if (bad_element == EL_ROBOT && player->is_moving)
13235           continue;     // robot does not kill player if he is moving
13236
13237         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13238         {
13239           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13240             continue;           // center and border element do not touch
13241         }
13242
13243         kill_x = test_x;
13244         kill_y = test_y;
13245
13246         break;
13247       }
13248       else if (test_element == EL_PENGUIN)
13249       {
13250         kill_x = test_x;
13251         kill_y = test_y;
13252
13253         break;
13254       }
13255     }
13256   }
13257
13258   if (kill_x != -1 || kill_y != -1)
13259   {
13260     if (IS_PLAYER(kill_x, kill_y))
13261     {
13262       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13263
13264       if (player->shield_deadly_time_left > 0 &&
13265           !IS_INDESTRUCTIBLE(bad_element))
13266         Bang(bad_x, bad_y);
13267       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13268         KillPlayer(player);
13269     }
13270     else
13271       Bang(kill_x, kill_y);
13272   }
13273 }
13274
13275 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13276 {
13277   int bad_element = Feld[bad_x][bad_y];
13278   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13279   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13280   int test_x = bad_x + dx, test_y = bad_y + dy;
13281   int test_move_dir, test_element;
13282   int kill_x = -1, kill_y = -1;
13283
13284   if (!IN_LEV_FIELD(test_x, test_y))
13285     return;
13286
13287   test_move_dir =
13288     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13289
13290   test_element = Feld[test_x][test_y];
13291
13292   if (test_move_dir != bad_move_dir)
13293   {
13294     // good thing can be player or penguin that does not move away
13295     if (IS_PLAYER(test_x, test_y))
13296     {
13297       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13298
13299       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13300          player as being hit when he is moving towards the bad thing, because
13301          the "get hit by" condition would be lost after the player stops) */
13302       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13303         return;         // player moves away from bad thing
13304
13305       kill_x = test_x;
13306       kill_y = test_y;
13307     }
13308     else if (test_element == EL_PENGUIN)
13309     {
13310       kill_x = test_x;
13311       kill_y = test_y;
13312     }
13313   }
13314
13315   if (kill_x != -1 || kill_y != -1)
13316   {
13317     if (IS_PLAYER(kill_x, kill_y))
13318     {
13319       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13320
13321       if (player->shield_deadly_time_left > 0 &&
13322           !IS_INDESTRUCTIBLE(bad_element))
13323         Bang(bad_x, bad_y);
13324       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13325         KillPlayer(player);
13326     }
13327     else
13328       Bang(kill_x, kill_y);
13329   }
13330 }
13331
13332 void TestIfPlayerTouchesBadThing(int x, int y)
13333 {
13334   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13335 }
13336
13337 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13338 {
13339   TestIfGoodThingHitsBadThing(x, y, move_dir);
13340 }
13341
13342 void TestIfBadThingTouchesPlayer(int x, int y)
13343 {
13344   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13345 }
13346
13347 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13348 {
13349   TestIfBadThingHitsGoodThing(x, y, move_dir);
13350 }
13351
13352 void TestIfFriendTouchesBadThing(int x, int y)
13353 {
13354   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13355 }
13356
13357 void TestIfBadThingTouchesFriend(int x, int y)
13358 {
13359   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13360 }
13361
13362 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13363 {
13364   int i, kill_x = bad_x, kill_y = bad_y;
13365   static int xy[4][2] =
13366   {
13367     { 0, -1 },
13368     { -1, 0 },
13369     { +1, 0 },
13370     { 0, +1 }
13371   };
13372
13373   for (i = 0; i < NUM_DIRECTIONS; i++)
13374   {
13375     int x, y, element;
13376
13377     x = bad_x + xy[i][0];
13378     y = bad_y + xy[i][1];
13379     if (!IN_LEV_FIELD(x, y))
13380       continue;
13381
13382     element = Feld[x][y];
13383     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13384         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13385     {
13386       kill_x = x;
13387       kill_y = y;
13388       break;
13389     }
13390   }
13391
13392   if (kill_x != bad_x || kill_y != bad_y)
13393     Bang(bad_x, bad_y);
13394 }
13395
13396 void KillPlayer(struct PlayerInfo *player)
13397 {
13398   int jx = player->jx, jy = player->jy;
13399
13400   if (!player->active)
13401     return;
13402
13403 #if 0
13404   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13405          player->killed, player->active, player->reanimated);
13406 #endif
13407
13408   /* the following code was introduced to prevent an infinite loop when calling
13409      -> Bang()
13410      -> CheckTriggeredElementChangeExt()
13411      -> ExecuteCustomElementAction()
13412      -> KillPlayer()
13413      -> (infinitely repeating the above sequence of function calls)
13414      which occurs when killing the player while having a CE with the setting
13415      "kill player X when explosion of <player X>"; the solution using a new
13416      field "player->killed" was chosen for backwards compatibility, although
13417      clever use of the fields "player->active" etc. would probably also work */
13418 #if 1
13419   if (player->killed)
13420     return;
13421 #endif
13422
13423   player->killed = TRUE;
13424
13425   // remove accessible field at the player's position
13426   Feld[jx][jy] = EL_EMPTY;
13427
13428   // deactivate shield (else Bang()/Explode() would not work right)
13429   player->shield_normal_time_left = 0;
13430   player->shield_deadly_time_left = 0;
13431
13432 #if 0
13433   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13434          player->killed, player->active, player->reanimated);
13435 #endif
13436
13437   Bang(jx, jy);
13438
13439 #if 0
13440   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13441          player->killed, player->active, player->reanimated);
13442 #endif
13443
13444   if (player->reanimated)       // killed player may have been reanimated
13445     player->killed = player->reanimated = FALSE;
13446   else
13447     BuryPlayer(player);
13448 }
13449
13450 static void KillPlayerUnlessEnemyProtected(int x, int y)
13451 {
13452   if (!PLAYER_ENEMY_PROTECTED(x, y))
13453     KillPlayer(PLAYERINFO(x, y));
13454 }
13455
13456 static void KillPlayerUnlessExplosionProtected(int x, int y)
13457 {
13458   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13459     KillPlayer(PLAYERINFO(x, y));
13460 }
13461
13462 void BuryPlayer(struct PlayerInfo *player)
13463 {
13464   int jx = player->jx, jy = player->jy;
13465
13466   if (!player->active)
13467     return;
13468
13469   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13470   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13471
13472   RemovePlayer(player);
13473
13474   player->buried = TRUE;
13475
13476   if (game.all_players_gone)
13477     game.GameOver = TRUE;
13478 }
13479
13480 void RemovePlayer(struct PlayerInfo *player)
13481 {
13482   int jx = player->jx, jy = player->jy;
13483   int i, found = FALSE;
13484
13485   player->present = FALSE;
13486   player->active = FALSE;
13487
13488   // required for some CE actions (even if the player is not active anymore)
13489   player->MovPos = 0;
13490
13491   if (!ExplodeField[jx][jy])
13492     StorePlayer[jx][jy] = 0;
13493
13494   if (player->is_moving)
13495     TEST_DrawLevelField(player->last_jx, player->last_jy);
13496
13497   for (i = 0; i < MAX_PLAYERS; i++)
13498     if (stored_player[i].active)
13499       found = TRUE;
13500
13501   if (!found)
13502   {
13503     game.all_players_gone = TRUE;
13504     game.GameOver = TRUE;
13505   }
13506
13507   game.exit_x = game.robot_wheel_x = jx;
13508   game.exit_y = game.robot_wheel_y = jy;
13509 }
13510
13511 void ExitPlayer(struct PlayerInfo *player)
13512 {
13513   DrawPlayer(player);   // needed here only to cleanup last field
13514   RemovePlayer(player);
13515
13516   if (game.players_still_needed > 0)
13517     game.players_still_needed--;
13518 }
13519
13520 static void setFieldForSnapping(int x, int y, int element, int direction)
13521 {
13522   struct ElementInfo *ei = &element_info[element];
13523   int direction_bit = MV_DIR_TO_BIT(direction);
13524   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13525   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13526                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13527
13528   Feld[x][y] = EL_ELEMENT_SNAPPING;
13529   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13530
13531   ResetGfxAnimation(x, y);
13532
13533   GfxElement[x][y] = element;
13534   GfxAction[x][y] = action;
13535   GfxDir[x][y] = direction;
13536   GfxFrame[x][y] = -1;
13537 }
13538
13539 /*
13540   =============================================================================
13541   checkDiagonalPushing()
13542   -----------------------------------------------------------------------------
13543   check if diagonal input device direction results in pushing of object
13544   (by checking if the alternative direction is walkable, diggable, ...)
13545   =============================================================================
13546 */
13547
13548 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13549                                     int x, int y, int real_dx, int real_dy)
13550 {
13551   int jx, jy, dx, dy, xx, yy;
13552
13553   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13554     return TRUE;
13555
13556   // diagonal direction: check alternative direction
13557   jx = player->jx;
13558   jy = player->jy;
13559   dx = x - jx;
13560   dy = y - jy;
13561   xx = jx + (dx == 0 ? real_dx : 0);
13562   yy = jy + (dy == 0 ? real_dy : 0);
13563
13564   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13565 }
13566
13567 /*
13568   =============================================================================
13569   DigField()
13570   -----------------------------------------------------------------------------
13571   x, y:                 field next to player (non-diagonal) to try to dig to
13572   real_dx, real_dy:     direction as read from input device (can be diagonal)
13573   =============================================================================
13574 */
13575
13576 static int DigField(struct PlayerInfo *player,
13577                     int oldx, int oldy, int x, int y,
13578                     int real_dx, int real_dy, int mode)
13579 {
13580   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13581   boolean player_was_pushing = player->is_pushing;
13582   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13583   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13584   int jx = oldx, jy = oldy;
13585   int dx = x - jx, dy = y - jy;
13586   int nextx = x + dx, nexty = y + dy;
13587   int move_direction = (dx == -1 ? MV_LEFT  :
13588                         dx == +1 ? MV_RIGHT :
13589                         dy == -1 ? MV_UP    :
13590                         dy == +1 ? MV_DOWN  : MV_NONE);
13591   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13592   int dig_side = MV_DIR_OPPOSITE(move_direction);
13593   int old_element = Feld[jx][jy];
13594   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13595   int collect_count;
13596
13597   if (is_player)                // function can also be called by EL_PENGUIN
13598   {
13599     if (player->MovPos == 0)
13600     {
13601       player->is_digging = FALSE;
13602       player->is_collecting = FALSE;
13603     }
13604
13605     if (player->MovPos == 0)    // last pushing move finished
13606       player->is_pushing = FALSE;
13607
13608     if (mode == DF_NO_PUSH)     // player just stopped pushing
13609     {
13610       player->is_switching = FALSE;
13611       player->push_delay = -1;
13612
13613       return MP_NO_ACTION;
13614     }
13615   }
13616
13617   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13618     old_element = Back[jx][jy];
13619
13620   // in case of element dropped at player position, check background
13621   else if (Back[jx][jy] != EL_EMPTY &&
13622            game.engine_version >= VERSION_IDENT(2,2,0,0))
13623     old_element = Back[jx][jy];
13624
13625   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13626     return MP_NO_ACTION;        // field has no opening in this direction
13627
13628   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13629     return MP_NO_ACTION;        // field has no opening in this direction
13630
13631   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13632   {
13633     SplashAcid(x, y);
13634
13635     Feld[jx][jy] = player->artwork_element;
13636     InitMovingField(jx, jy, MV_DOWN);
13637     Store[jx][jy] = EL_ACID;
13638     ContinueMoving(jx, jy);
13639     BuryPlayer(player);
13640
13641     return MP_DONT_RUN_INTO;
13642   }
13643
13644   if (player_can_move && DONT_RUN_INTO(element))
13645   {
13646     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13647
13648     return MP_DONT_RUN_INTO;
13649   }
13650
13651   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13652     return MP_NO_ACTION;
13653
13654   collect_count = element_info[element].collect_count_initial;
13655
13656   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13657     return MP_NO_ACTION;
13658
13659   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13660     player_can_move = player_can_move_or_snap;
13661
13662   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13663       game.engine_version >= VERSION_IDENT(2,2,0,0))
13664   {
13665     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13666                                player->index_bit, dig_side);
13667     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13668                                         player->index_bit, dig_side);
13669
13670     if (element == EL_DC_LANDMINE)
13671       Bang(x, y);
13672
13673     if (Feld[x][y] != element)          // field changed by snapping
13674       return MP_ACTION;
13675
13676     return MP_NO_ACTION;
13677   }
13678
13679   if (player->gravity && is_player && !player->is_auto_moving &&
13680       canFallDown(player) && move_direction != MV_DOWN &&
13681       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13682     return MP_NO_ACTION;        // player cannot walk here due to gravity
13683
13684   if (player_can_move &&
13685       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13686   {
13687     int sound_element = SND_ELEMENT(element);
13688     int sound_action = ACTION_WALKING;
13689
13690     if (IS_RND_GATE(element))
13691     {
13692       if (!player->key[RND_GATE_NR(element)])
13693         return MP_NO_ACTION;
13694     }
13695     else if (IS_RND_GATE_GRAY(element))
13696     {
13697       if (!player->key[RND_GATE_GRAY_NR(element)])
13698         return MP_NO_ACTION;
13699     }
13700     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13701     {
13702       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13703         return MP_NO_ACTION;
13704     }
13705     else if (element == EL_EXIT_OPEN ||
13706              element == EL_EM_EXIT_OPEN ||
13707              element == EL_EM_EXIT_OPENING ||
13708              element == EL_STEEL_EXIT_OPEN ||
13709              element == EL_EM_STEEL_EXIT_OPEN ||
13710              element == EL_EM_STEEL_EXIT_OPENING ||
13711              element == EL_SP_EXIT_OPEN ||
13712              element == EL_SP_EXIT_OPENING)
13713     {
13714       sound_action = ACTION_PASSING;    // player is passing exit
13715     }
13716     else if (element == EL_EMPTY)
13717     {
13718       sound_action = ACTION_MOVING;             // nothing to walk on
13719     }
13720
13721     // play sound from background or player, whatever is available
13722     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13723       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13724     else
13725       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13726   }
13727   else if (player_can_move &&
13728            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13729   {
13730     if (!ACCESS_FROM(element, opposite_direction))
13731       return MP_NO_ACTION;      // field not accessible from this direction
13732
13733     if (CAN_MOVE(element))      // only fixed elements can be passed!
13734       return MP_NO_ACTION;
13735
13736     if (IS_EM_GATE(element))
13737     {
13738       if (!player->key[EM_GATE_NR(element)])
13739         return MP_NO_ACTION;
13740     }
13741     else if (IS_EM_GATE_GRAY(element))
13742     {
13743       if (!player->key[EM_GATE_GRAY_NR(element)])
13744         return MP_NO_ACTION;
13745     }
13746     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13747     {
13748       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13749         return MP_NO_ACTION;
13750     }
13751     else if (IS_EMC_GATE(element))
13752     {
13753       if (!player->key[EMC_GATE_NR(element)])
13754         return MP_NO_ACTION;
13755     }
13756     else if (IS_EMC_GATE_GRAY(element))
13757     {
13758       if (!player->key[EMC_GATE_GRAY_NR(element)])
13759         return MP_NO_ACTION;
13760     }
13761     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13762     {
13763       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13764         return MP_NO_ACTION;
13765     }
13766     else if (element == EL_DC_GATE_WHITE ||
13767              element == EL_DC_GATE_WHITE_GRAY ||
13768              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13769     {
13770       if (player->num_white_keys == 0)
13771         return MP_NO_ACTION;
13772
13773       player->num_white_keys--;
13774     }
13775     else if (IS_SP_PORT(element))
13776     {
13777       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13778           element == EL_SP_GRAVITY_PORT_RIGHT ||
13779           element == EL_SP_GRAVITY_PORT_UP ||
13780           element == EL_SP_GRAVITY_PORT_DOWN)
13781         player->gravity = !player->gravity;
13782       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13783                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13784                element == EL_SP_GRAVITY_ON_PORT_UP ||
13785                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13786         player->gravity = TRUE;
13787       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13788                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13789                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13790                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13791         player->gravity = FALSE;
13792     }
13793
13794     // automatically move to the next field with double speed
13795     player->programmed_action = move_direction;
13796
13797     if (player->move_delay_reset_counter == 0)
13798     {
13799       player->move_delay_reset_counter = 2;     // two double speed steps
13800
13801       DOUBLE_PLAYER_SPEED(player);
13802     }
13803
13804     PlayLevelSoundAction(x, y, ACTION_PASSING);
13805   }
13806   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13807   {
13808     RemoveField(x, y);
13809
13810     if (mode != DF_SNAP)
13811     {
13812       GfxElement[x][y] = GFX_ELEMENT(element);
13813       player->is_digging = TRUE;
13814     }
13815
13816     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13817
13818     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13819                                         player->index_bit, dig_side);
13820
13821     if (mode == DF_SNAP)
13822     {
13823       if (level.block_snap_field)
13824         setFieldForSnapping(x, y, element, move_direction);
13825       else
13826         TestIfElementTouchesCustomElement(x, y);        // for empty space
13827
13828       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13829                                           player->index_bit, dig_side);
13830     }
13831   }
13832   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13833   {
13834     RemoveField(x, y);
13835
13836     if (is_player && mode != DF_SNAP)
13837     {
13838       GfxElement[x][y] = element;
13839       player->is_collecting = TRUE;
13840     }
13841
13842     if (element == EL_SPEED_PILL)
13843     {
13844       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13845     }
13846     else if (element == EL_EXTRA_TIME && level.time > 0)
13847     {
13848       TimeLeft += level.extra_time;
13849
13850       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13851
13852       DisplayGameControlValues();
13853     }
13854     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13855     {
13856       player->shield_normal_time_left += level.shield_normal_time;
13857       if (element == EL_SHIELD_DEADLY)
13858         player->shield_deadly_time_left += level.shield_deadly_time;
13859     }
13860     else if (element == EL_DYNAMITE ||
13861              element == EL_EM_DYNAMITE ||
13862              element == EL_SP_DISK_RED)
13863     {
13864       if (player->inventory_size < MAX_INVENTORY_SIZE)
13865         player->inventory_element[player->inventory_size++] = element;
13866
13867       DrawGameDoorValues();
13868     }
13869     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13870     {
13871       player->dynabomb_count++;
13872       player->dynabombs_left++;
13873     }
13874     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13875     {
13876       player->dynabomb_size++;
13877     }
13878     else if (element == EL_DYNABOMB_INCREASE_POWER)
13879     {
13880       player->dynabomb_xl = TRUE;
13881     }
13882     else if (IS_KEY(element))
13883     {
13884       player->key[KEY_NR(element)] = TRUE;
13885
13886       DrawGameDoorValues();
13887     }
13888     else if (element == EL_DC_KEY_WHITE)
13889     {
13890       player->num_white_keys++;
13891
13892       // display white keys?
13893       // DrawGameDoorValues();
13894     }
13895     else if (IS_ENVELOPE(element))
13896     {
13897       player->show_envelope = element;
13898     }
13899     else if (element == EL_EMC_LENSES)
13900     {
13901       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13902
13903       RedrawAllInvisibleElementsForLenses();
13904     }
13905     else if (element == EL_EMC_MAGNIFIER)
13906     {
13907       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13908
13909       RedrawAllInvisibleElementsForMagnifier();
13910     }
13911     else if (IS_DROPPABLE(element) ||
13912              IS_THROWABLE(element))     // can be collected and dropped
13913     {
13914       int i;
13915
13916       if (collect_count == 0)
13917         player->inventory_infinite_element = element;
13918       else
13919         for (i = 0; i < collect_count; i++)
13920           if (player->inventory_size < MAX_INVENTORY_SIZE)
13921             player->inventory_element[player->inventory_size++] = element;
13922
13923       DrawGameDoorValues();
13924     }
13925     else if (collect_count > 0)
13926     {
13927       game.gems_still_needed -= collect_count;
13928       if (game.gems_still_needed < 0)
13929         game.gems_still_needed = 0;
13930
13931       game.snapshot.collected_item = TRUE;
13932
13933       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
13934
13935       DisplayGameControlValues();
13936     }
13937
13938     RaiseScoreElement(element);
13939     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13940
13941     if (is_player)
13942       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13943                                           player->index_bit, dig_side);
13944
13945     if (mode == DF_SNAP)
13946     {
13947       if (level.block_snap_field)
13948         setFieldForSnapping(x, y, element, move_direction);
13949       else
13950         TestIfElementTouchesCustomElement(x, y);        // for empty space
13951
13952       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13953                                           player->index_bit, dig_side);
13954     }
13955   }
13956   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13957   {
13958     if (mode == DF_SNAP && element != EL_BD_ROCK)
13959       return MP_NO_ACTION;
13960
13961     if (CAN_FALL(element) && dy)
13962       return MP_NO_ACTION;
13963
13964     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13965         !(element == EL_SPRING && level.use_spring_bug))
13966       return MP_NO_ACTION;
13967
13968     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13969         ((move_direction & MV_VERTICAL &&
13970           ((element_info[element].move_pattern & MV_LEFT &&
13971             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13972            (element_info[element].move_pattern & MV_RIGHT &&
13973             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13974          (move_direction & MV_HORIZONTAL &&
13975           ((element_info[element].move_pattern & MV_UP &&
13976             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13977            (element_info[element].move_pattern & MV_DOWN &&
13978             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13979       return MP_NO_ACTION;
13980
13981     // do not push elements already moving away faster than player
13982     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13983         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13984       return MP_NO_ACTION;
13985
13986     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13987     {
13988       if (player->push_delay_value == -1 || !player_was_pushing)
13989         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13990     }
13991     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13992     {
13993       if (player->push_delay_value == -1)
13994         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13995     }
13996     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13997     {
13998       if (!player->is_pushing)
13999         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14000     }
14001
14002     player->is_pushing = TRUE;
14003     player->is_active = TRUE;
14004
14005     if (!(IN_LEV_FIELD(nextx, nexty) &&
14006           (IS_FREE(nextx, nexty) ||
14007            (IS_SB_ELEMENT(element) &&
14008             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14009            (IS_CUSTOM_ELEMENT(element) &&
14010             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14011       return MP_NO_ACTION;
14012
14013     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14014       return MP_NO_ACTION;
14015
14016     if (player->push_delay == -1)       // new pushing; restart delay
14017       player->push_delay = 0;
14018
14019     if (player->push_delay < player->push_delay_value &&
14020         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14021         element != EL_SPRING && element != EL_BALLOON)
14022     {
14023       // make sure that there is no move delay before next try to push
14024       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14025         player->move_delay = 0;
14026
14027       return MP_NO_ACTION;
14028     }
14029
14030     if (IS_CUSTOM_ELEMENT(element) &&
14031         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14032     {
14033       if (!DigFieldByCE(nextx, nexty, element))
14034         return MP_NO_ACTION;
14035     }
14036
14037     if (IS_SB_ELEMENT(element))
14038     {
14039       boolean sokoban_task_solved = FALSE;
14040
14041       if (element == EL_SOKOBAN_FIELD_FULL)
14042       {
14043         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14044
14045         IncrementSokobanFieldsNeeded();
14046         IncrementSokobanObjectsNeeded();
14047       }
14048
14049       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14050       {
14051         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14052
14053         DecrementSokobanFieldsNeeded();
14054         DecrementSokobanObjectsNeeded();
14055
14056         // sokoban object was pushed from empty field to sokoban field
14057         if (Back[x][y] == EL_EMPTY)
14058           sokoban_task_solved = TRUE;
14059       }
14060
14061       Feld[x][y] = EL_SOKOBAN_OBJECT;
14062
14063       if (Back[x][y] == Back[nextx][nexty])
14064         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14065       else if (Back[x][y] != 0)
14066         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14067                                     ACTION_EMPTYING);
14068       else
14069         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14070                                     ACTION_FILLING);
14071
14072       if (sokoban_task_solved &&
14073           game.sokoban_fields_still_needed == 0 &&
14074           game.sokoban_objects_still_needed == 0 &&
14075           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14076       {
14077         game.players_still_needed = 0;
14078
14079         LevelSolved();
14080
14081         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14082       }
14083     }
14084     else
14085       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14086
14087     InitMovingField(x, y, move_direction);
14088     GfxAction[x][y] = ACTION_PUSHING;
14089
14090     if (mode == DF_SNAP)
14091       ContinueMoving(x, y);
14092     else
14093       MovPos[x][y] = (dx != 0 ? dx : dy);
14094
14095     Pushed[x][y] = TRUE;
14096     Pushed[nextx][nexty] = TRUE;
14097
14098     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14099       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14100     else
14101       player->push_delay_value = -1;    // get new value later
14102
14103     // check for element change _after_ element has been pushed
14104     if (game.use_change_when_pushing_bug)
14105     {
14106       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14107                                  player->index_bit, dig_side);
14108       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14109                                           player->index_bit, dig_side);
14110     }
14111   }
14112   else if (IS_SWITCHABLE(element))
14113   {
14114     if (PLAYER_SWITCHING(player, x, y))
14115     {
14116       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14117                                           player->index_bit, dig_side);
14118
14119       return MP_ACTION;
14120     }
14121
14122     player->is_switching = TRUE;
14123     player->switch_x = x;
14124     player->switch_y = y;
14125
14126     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14127
14128     if (element == EL_ROBOT_WHEEL)
14129     {
14130       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14131
14132       game.robot_wheel_x = x;
14133       game.robot_wheel_y = y;
14134       game.robot_wheel_active = TRUE;
14135
14136       TEST_DrawLevelField(x, y);
14137     }
14138     else if (element == EL_SP_TERMINAL)
14139     {
14140       int xx, yy;
14141
14142       SCAN_PLAYFIELD(xx, yy)
14143       {
14144         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14145         {
14146           Bang(xx, yy);
14147         }
14148         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14149         {
14150           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14151
14152           ResetGfxAnimation(xx, yy);
14153           TEST_DrawLevelField(xx, yy);
14154         }
14155       }
14156     }
14157     else if (IS_BELT_SWITCH(element))
14158     {
14159       ToggleBeltSwitch(x, y);
14160     }
14161     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14162              element == EL_SWITCHGATE_SWITCH_DOWN ||
14163              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14164              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14165     {
14166       ToggleSwitchgateSwitch(x, y);
14167     }
14168     else if (element == EL_LIGHT_SWITCH ||
14169              element == EL_LIGHT_SWITCH_ACTIVE)
14170     {
14171       ToggleLightSwitch(x, y);
14172     }
14173     else if (element == EL_TIMEGATE_SWITCH ||
14174              element == EL_DC_TIMEGATE_SWITCH)
14175     {
14176       ActivateTimegateSwitch(x, y);
14177     }
14178     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14179              element == EL_BALLOON_SWITCH_RIGHT ||
14180              element == EL_BALLOON_SWITCH_UP    ||
14181              element == EL_BALLOON_SWITCH_DOWN  ||
14182              element == EL_BALLOON_SWITCH_NONE  ||
14183              element == EL_BALLOON_SWITCH_ANY)
14184     {
14185       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14186                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14187                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14188                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14189                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14190                              move_direction);
14191     }
14192     else if (element == EL_LAMP)
14193     {
14194       Feld[x][y] = EL_LAMP_ACTIVE;
14195       game.lights_still_needed--;
14196
14197       ResetGfxAnimation(x, y);
14198       TEST_DrawLevelField(x, y);
14199     }
14200     else if (element == EL_TIME_ORB_FULL)
14201     {
14202       Feld[x][y] = EL_TIME_ORB_EMPTY;
14203
14204       if (level.time > 0 || level.use_time_orb_bug)
14205       {
14206         TimeLeft += level.time_orb_time;
14207         game.no_time_limit = FALSE;
14208
14209         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14210
14211         DisplayGameControlValues();
14212       }
14213
14214       ResetGfxAnimation(x, y);
14215       TEST_DrawLevelField(x, y);
14216     }
14217     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14218              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14219     {
14220       int xx, yy;
14221
14222       game.ball_state = !game.ball_state;
14223
14224       SCAN_PLAYFIELD(xx, yy)
14225       {
14226         int e = Feld[xx][yy];
14227
14228         if (game.ball_state)
14229         {
14230           if (e == EL_EMC_MAGIC_BALL)
14231             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14232           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14233             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14234         }
14235         else
14236         {
14237           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14238             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14239           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14240             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14241         }
14242       }
14243     }
14244
14245     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14246                                         player->index_bit, dig_side);
14247
14248     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14249                                         player->index_bit, dig_side);
14250
14251     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14252                                         player->index_bit, dig_side);
14253
14254     return MP_ACTION;
14255   }
14256   else
14257   {
14258     if (!PLAYER_SWITCHING(player, x, y))
14259     {
14260       player->is_switching = TRUE;
14261       player->switch_x = x;
14262       player->switch_y = y;
14263
14264       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14265                                  player->index_bit, dig_side);
14266       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14267                                           player->index_bit, dig_side);
14268
14269       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14270                                  player->index_bit, dig_side);
14271       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14272                                           player->index_bit, dig_side);
14273     }
14274
14275     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14276                                player->index_bit, dig_side);
14277     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14278                                         player->index_bit, dig_side);
14279
14280     return MP_NO_ACTION;
14281   }
14282
14283   player->push_delay = -1;
14284
14285   if (is_player)                // function can also be called by EL_PENGUIN
14286   {
14287     if (Feld[x][y] != element)          // really digged/collected something
14288     {
14289       player->is_collecting = !player->is_digging;
14290       player->is_active = TRUE;
14291     }
14292   }
14293
14294   return MP_MOVING;
14295 }
14296
14297 static boolean DigFieldByCE(int x, int y, int digging_element)
14298 {
14299   int element = Feld[x][y];
14300
14301   if (!IS_FREE(x, y))
14302   {
14303     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14304                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14305                   ACTION_BREAKING);
14306
14307     // no element can dig solid indestructible elements
14308     if (IS_INDESTRUCTIBLE(element) &&
14309         !IS_DIGGABLE(element) &&
14310         !IS_COLLECTIBLE(element))
14311       return FALSE;
14312
14313     if (AmoebaNr[x][y] &&
14314         (element == EL_AMOEBA_FULL ||
14315          element == EL_BD_AMOEBA ||
14316          element == EL_AMOEBA_GROWING))
14317     {
14318       AmoebaCnt[AmoebaNr[x][y]]--;
14319       AmoebaCnt2[AmoebaNr[x][y]]--;
14320     }
14321
14322     if (IS_MOVING(x, y))
14323       RemoveMovingField(x, y);
14324     else
14325     {
14326       RemoveField(x, y);
14327       TEST_DrawLevelField(x, y);
14328     }
14329
14330     // if digged element was about to explode, prevent the explosion
14331     ExplodeField[x][y] = EX_TYPE_NONE;
14332
14333     PlayLevelSoundAction(x, y, action);
14334   }
14335
14336   Store[x][y] = EL_EMPTY;
14337
14338   // this makes it possible to leave the removed element again
14339   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14340     Store[x][y] = element;
14341
14342   return TRUE;
14343 }
14344
14345 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14346 {
14347   int jx = player->jx, jy = player->jy;
14348   int x = jx + dx, y = jy + dy;
14349   int snap_direction = (dx == -1 ? MV_LEFT  :
14350                         dx == +1 ? MV_RIGHT :
14351                         dy == -1 ? MV_UP    :
14352                         dy == +1 ? MV_DOWN  : MV_NONE);
14353   boolean can_continue_snapping = (level.continuous_snapping &&
14354                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14355
14356   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14357     return FALSE;
14358
14359   if (!player->active || !IN_LEV_FIELD(x, y))
14360     return FALSE;
14361
14362   if (dx && dy)
14363     return FALSE;
14364
14365   if (!dx && !dy)
14366   {
14367     if (player->MovPos == 0)
14368       player->is_pushing = FALSE;
14369
14370     player->is_snapping = FALSE;
14371
14372     if (player->MovPos == 0)
14373     {
14374       player->is_moving = FALSE;
14375       player->is_digging = FALSE;
14376       player->is_collecting = FALSE;
14377     }
14378
14379     return FALSE;
14380   }
14381
14382   // prevent snapping with already pressed snap key when not allowed
14383   if (player->is_snapping && !can_continue_snapping)
14384     return FALSE;
14385
14386   player->MovDir = snap_direction;
14387
14388   if (player->MovPos == 0)
14389   {
14390     player->is_moving = FALSE;
14391     player->is_digging = FALSE;
14392     player->is_collecting = FALSE;
14393   }
14394
14395   player->is_dropping = FALSE;
14396   player->is_dropping_pressed = FALSE;
14397   player->drop_pressed_delay = 0;
14398
14399   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14400     return FALSE;
14401
14402   player->is_snapping = TRUE;
14403   player->is_active = TRUE;
14404
14405   if (player->MovPos == 0)
14406   {
14407     player->is_moving = FALSE;
14408     player->is_digging = FALSE;
14409     player->is_collecting = FALSE;
14410   }
14411
14412   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14413     TEST_DrawLevelField(player->last_jx, player->last_jy);
14414
14415   TEST_DrawLevelField(x, y);
14416
14417   return TRUE;
14418 }
14419
14420 static boolean DropElement(struct PlayerInfo *player)
14421 {
14422   int old_element, new_element;
14423   int dropx = player->jx, dropy = player->jy;
14424   int drop_direction = player->MovDir;
14425   int drop_side = drop_direction;
14426   int drop_element = get_next_dropped_element(player);
14427
14428   /* do not drop an element on top of another element; when holding drop key
14429      pressed without moving, dropped element must move away before the next
14430      element can be dropped (this is especially important if the next element
14431      is dynamite, which can be placed on background for historical reasons) */
14432   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14433     return MP_ACTION;
14434
14435   if (IS_THROWABLE(drop_element))
14436   {
14437     dropx += GET_DX_FROM_DIR(drop_direction);
14438     dropy += GET_DY_FROM_DIR(drop_direction);
14439
14440     if (!IN_LEV_FIELD(dropx, dropy))
14441       return FALSE;
14442   }
14443
14444   old_element = Feld[dropx][dropy];     // old element at dropping position
14445   new_element = drop_element;           // default: no change when dropping
14446
14447   // check if player is active, not moving and ready to drop
14448   if (!player->active || player->MovPos || player->drop_delay > 0)
14449     return FALSE;
14450
14451   // check if player has anything that can be dropped
14452   if (new_element == EL_UNDEFINED)
14453     return FALSE;
14454
14455   // only set if player has anything that can be dropped
14456   player->is_dropping_pressed = TRUE;
14457
14458   // check if drop key was pressed long enough for EM style dynamite
14459   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14460     return FALSE;
14461
14462   // check if anything can be dropped at the current position
14463   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14464     return FALSE;
14465
14466   // collected custom elements can only be dropped on empty fields
14467   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14468     return FALSE;
14469
14470   if (old_element != EL_EMPTY)
14471     Back[dropx][dropy] = old_element;   // store old element on this field
14472
14473   ResetGfxAnimation(dropx, dropy);
14474   ResetRandomAnimationValue(dropx, dropy);
14475
14476   if (player->inventory_size > 0 ||
14477       player->inventory_infinite_element != EL_UNDEFINED)
14478   {
14479     if (player->inventory_size > 0)
14480     {
14481       player->inventory_size--;
14482
14483       DrawGameDoorValues();
14484
14485       if (new_element == EL_DYNAMITE)
14486         new_element = EL_DYNAMITE_ACTIVE;
14487       else if (new_element == EL_EM_DYNAMITE)
14488         new_element = EL_EM_DYNAMITE_ACTIVE;
14489       else if (new_element == EL_SP_DISK_RED)
14490         new_element = EL_SP_DISK_RED_ACTIVE;
14491     }
14492
14493     Feld[dropx][dropy] = new_element;
14494
14495     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14496       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14497                           el2img(Feld[dropx][dropy]), 0);
14498
14499     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14500
14501     // needed if previous element just changed to "empty" in the last frame
14502     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14503
14504     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14505                                player->index_bit, drop_side);
14506     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14507                                         CE_PLAYER_DROPS_X,
14508                                         player->index_bit, drop_side);
14509
14510     TestIfElementTouchesCustomElement(dropx, dropy);
14511   }
14512   else          // player is dropping a dyna bomb
14513   {
14514     player->dynabombs_left--;
14515
14516     Feld[dropx][dropy] = new_element;
14517
14518     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14519       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14520                           el2img(Feld[dropx][dropy]), 0);
14521
14522     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14523   }
14524
14525   if (Feld[dropx][dropy] == new_element) // uninitialized unless CE change
14526     InitField_WithBug1(dropx, dropy, FALSE);
14527
14528   new_element = Feld[dropx][dropy];     // element might have changed
14529
14530   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14531       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14532   {
14533     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14534       MovDir[dropx][dropy] = drop_direction;
14535
14536     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14537
14538     // do not cause impact style collision by dropping elements that can fall
14539     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14540   }
14541
14542   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14543   player->is_dropping = TRUE;
14544
14545   player->drop_pressed_delay = 0;
14546   player->is_dropping_pressed = FALSE;
14547
14548   player->drop_x = dropx;
14549   player->drop_y = dropy;
14550
14551   return TRUE;
14552 }
14553
14554 // ----------------------------------------------------------------------------
14555 // game sound playing functions
14556 // ----------------------------------------------------------------------------
14557
14558 static int *loop_sound_frame = NULL;
14559 static int *loop_sound_volume = NULL;
14560
14561 void InitPlayLevelSound(void)
14562 {
14563   int num_sounds = getSoundListSize();
14564
14565   checked_free(loop_sound_frame);
14566   checked_free(loop_sound_volume);
14567
14568   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14569   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14570 }
14571
14572 static void PlayLevelSound(int x, int y, int nr)
14573 {
14574   int sx = SCREENX(x), sy = SCREENY(y);
14575   int volume, stereo_position;
14576   int max_distance = 8;
14577   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14578
14579   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14580       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14581     return;
14582
14583   if (!IN_LEV_FIELD(x, y) ||
14584       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14585       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14586     return;
14587
14588   volume = SOUND_MAX_VOLUME;
14589
14590   if (!IN_SCR_FIELD(sx, sy))
14591   {
14592     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14593     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14594
14595     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14596   }
14597
14598   stereo_position = (SOUND_MAX_LEFT +
14599                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14600                      (SCR_FIELDX + 2 * max_distance));
14601
14602   if (IS_LOOP_SOUND(nr))
14603   {
14604     /* This assures that quieter loop sounds do not overwrite louder ones,
14605        while restarting sound volume comparison with each new game frame. */
14606
14607     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14608       return;
14609
14610     loop_sound_volume[nr] = volume;
14611     loop_sound_frame[nr] = FrameCounter;
14612   }
14613
14614   PlaySoundExt(nr, volume, stereo_position, type);
14615 }
14616
14617 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14618 {
14619   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14620                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14621                  y < LEVELY(BY1) ? LEVELY(BY1) :
14622                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14623                  sound_action);
14624 }
14625
14626 static void PlayLevelSoundAction(int x, int y, int action)
14627 {
14628   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14629 }
14630
14631 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14632 {
14633   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14634
14635   if (sound_effect != SND_UNDEFINED)
14636     PlayLevelSound(x, y, sound_effect);
14637 }
14638
14639 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14640                                               int action)
14641 {
14642   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14643
14644   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14645     PlayLevelSound(x, y, sound_effect);
14646 }
14647
14648 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14649 {
14650   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14651
14652   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14653     PlayLevelSound(x, y, sound_effect);
14654 }
14655
14656 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14657 {
14658   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14659
14660   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14661     StopSound(sound_effect);
14662 }
14663
14664 static int getLevelMusicNr(void)
14665 {
14666   if (levelset.music[level_nr] != MUS_UNDEFINED)
14667     return levelset.music[level_nr];            // from config file
14668   else
14669     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
14670 }
14671
14672 static void FadeLevelSounds(void)
14673 {
14674   FadeSounds();
14675 }
14676
14677 static void FadeLevelMusic(void)
14678 {
14679   int music_nr = getLevelMusicNr();
14680   char *curr_music = getCurrentlyPlayingMusicFilename();
14681   char *next_music = getMusicInfoEntryFilename(music_nr);
14682
14683   if (!strEqual(curr_music, next_music))
14684     FadeMusic();
14685 }
14686
14687 void FadeLevelSoundsAndMusic(void)
14688 {
14689   FadeLevelSounds();
14690   FadeLevelMusic();
14691 }
14692
14693 static void PlayLevelMusic(void)
14694 {
14695   int music_nr = getLevelMusicNr();
14696   char *curr_music = getCurrentlyPlayingMusicFilename();
14697   char *next_music = getMusicInfoEntryFilename(music_nr);
14698
14699   if (!strEqual(curr_music, next_music))
14700     PlayMusicLoop(music_nr);
14701 }
14702
14703 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14704 {
14705   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14706   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14707   int x = xx - 1 - offset;
14708   int y = yy - 1 - offset;
14709
14710   switch (sample)
14711   {
14712     case SOUND_blank:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14714       break;
14715
14716     case SOUND_roll:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14718       break;
14719
14720     case SOUND_stone:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14722       break;
14723
14724     case SOUND_nut:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14726       break;
14727
14728     case SOUND_crack:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14730       break;
14731
14732     case SOUND_bug:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14734       break;
14735
14736     case SOUND_tank:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14738       break;
14739
14740     case SOUND_android_clone:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14742       break;
14743
14744     case SOUND_android_move:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14746       break;
14747
14748     case SOUND_spring:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14750       break;
14751
14752     case SOUND_slurp:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14754       break;
14755
14756     case SOUND_eater:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14758       break;
14759
14760     case SOUND_eater_eat:
14761       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14762       break;
14763
14764     case SOUND_alien:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14766       break;
14767
14768     case SOUND_collect:
14769       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14770       break;
14771
14772     case SOUND_diamond:
14773       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14774       break;
14775
14776     case SOUND_squash:
14777       // !!! CHECK THIS !!!
14778 #if 1
14779       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14780 #else
14781       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14782 #endif
14783       break;
14784
14785     case SOUND_wonderfall:
14786       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14787       break;
14788
14789     case SOUND_drip:
14790       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14791       break;
14792
14793     case SOUND_push:
14794       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14795       break;
14796
14797     case SOUND_dirt:
14798       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14799       break;
14800
14801     case SOUND_acid:
14802       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14803       break;
14804
14805     case SOUND_ball:
14806       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14807       break;
14808
14809     case SOUND_slidewall:
14810       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14811       break;
14812
14813     case SOUND_wonder:
14814       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14815       break;
14816
14817     case SOUND_door:
14818       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14819       break;
14820
14821     case SOUND_exit_open:
14822       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14823       break;
14824
14825     case SOUND_exit_leave:
14826       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14827       break;
14828
14829     case SOUND_dynamite:
14830       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14831       break;
14832
14833     case SOUND_tick:
14834       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14835       break;
14836
14837     case SOUND_press:
14838       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14839       break;
14840
14841     case SOUND_wheel:
14842       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14843       break;
14844
14845     case SOUND_boom:
14846       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14847       break;
14848
14849     case SOUND_die:
14850       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14851       break;
14852
14853     case SOUND_time:
14854       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14855       break;
14856
14857     default:
14858       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14859       break;
14860   }
14861 }
14862
14863 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14864 {
14865   int element = map_element_SP_to_RND(element_sp);
14866   int action = map_action_SP_to_RND(action_sp);
14867   int offset = (setup.sp_show_border_elements ? 0 : 1);
14868   int x = xx - offset;
14869   int y = yy - offset;
14870
14871   PlayLevelSoundElementAction(x, y, element, action);
14872 }
14873
14874 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14875 {
14876   int element = map_element_MM_to_RND(element_mm);
14877   int action = map_action_MM_to_RND(action_mm);
14878   int offset = 0;
14879   int x = xx - offset;
14880   int y = yy - offset;
14881
14882   if (!IS_MM_ELEMENT(element))
14883     element = EL_MM_DEFAULT;
14884
14885   PlayLevelSoundElementAction(x, y, element, action);
14886 }
14887
14888 void PlaySound_MM(int sound_mm)
14889 {
14890   int sound = map_sound_MM_to_RND(sound_mm);
14891
14892   if (sound == SND_UNDEFINED)
14893     return;
14894
14895   PlaySound(sound);
14896 }
14897
14898 void PlaySoundLoop_MM(int sound_mm)
14899 {
14900   int sound = map_sound_MM_to_RND(sound_mm);
14901
14902   if (sound == SND_UNDEFINED)
14903     return;
14904
14905   PlaySoundLoop(sound);
14906 }
14907
14908 void StopSound_MM(int sound_mm)
14909 {
14910   int sound = map_sound_MM_to_RND(sound_mm);
14911
14912   if (sound == SND_UNDEFINED)
14913     return;
14914
14915   StopSound(sound);
14916 }
14917
14918 void RaiseScore(int value)
14919 {
14920   game.score += value;
14921
14922   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
14923
14924   DisplayGameControlValues();
14925 }
14926
14927 void RaiseScoreElement(int element)
14928 {
14929   switch (element)
14930   {
14931     case EL_EMERALD:
14932     case EL_BD_DIAMOND:
14933     case EL_EMERALD_YELLOW:
14934     case EL_EMERALD_RED:
14935     case EL_EMERALD_PURPLE:
14936     case EL_SP_INFOTRON:
14937       RaiseScore(level.score[SC_EMERALD]);
14938       break;
14939     case EL_DIAMOND:
14940       RaiseScore(level.score[SC_DIAMOND]);
14941       break;
14942     case EL_CRYSTAL:
14943       RaiseScore(level.score[SC_CRYSTAL]);
14944       break;
14945     case EL_PEARL:
14946       RaiseScore(level.score[SC_PEARL]);
14947       break;
14948     case EL_BUG:
14949     case EL_BD_BUTTERFLY:
14950     case EL_SP_ELECTRON:
14951       RaiseScore(level.score[SC_BUG]);
14952       break;
14953     case EL_SPACESHIP:
14954     case EL_BD_FIREFLY:
14955     case EL_SP_SNIKSNAK:
14956       RaiseScore(level.score[SC_SPACESHIP]);
14957       break;
14958     case EL_YAMYAM:
14959     case EL_DARK_YAMYAM:
14960       RaiseScore(level.score[SC_YAMYAM]);
14961       break;
14962     case EL_ROBOT:
14963       RaiseScore(level.score[SC_ROBOT]);
14964       break;
14965     case EL_PACMAN:
14966       RaiseScore(level.score[SC_PACMAN]);
14967       break;
14968     case EL_NUT:
14969       RaiseScore(level.score[SC_NUT]);
14970       break;
14971     case EL_DYNAMITE:
14972     case EL_EM_DYNAMITE:
14973     case EL_SP_DISK_RED:
14974     case EL_DYNABOMB_INCREASE_NUMBER:
14975     case EL_DYNABOMB_INCREASE_SIZE:
14976     case EL_DYNABOMB_INCREASE_POWER:
14977       RaiseScore(level.score[SC_DYNAMITE]);
14978       break;
14979     case EL_SHIELD_NORMAL:
14980     case EL_SHIELD_DEADLY:
14981       RaiseScore(level.score[SC_SHIELD]);
14982       break;
14983     case EL_EXTRA_TIME:
14984       RaiseScore(level.extra_time_score);
14985       break;
14986     case EL_KEY_1:
14987     case EL_KEY_2:
14988     case EL_KEY_3:
14989     case EL_KEY_4:
14990     case EL_EM_KEY_1:
14991     case EL_EM_KEY_2:
14992     case EL_EM_KEY_3:
14993     case EL_EM_KEY_4:
14994     case EL_EMC_KEY_5:
14995     case EL_EMC_KEY_6:
14996     case EL_EMC_KEY_7:
14997     case EL_EMC_KEY_8:
14998     case EL_DC_KEY_WHITE:
14999       RaiseScore(level.score[SC_KEY]);
15000       break;
15001     default:
15002       RaiseScore(element_info[element].collect_score);
15003       break;
15004   }
15005 }
15006
15007 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15008 {
15009   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15010   {
15011     // closing door required in case of envelope style request dialogs
15012     if (!skip_request)
15013     {
15014       // prevent short reactivation of overlay buttons while closing door
15015       SetOverlayActive(FALSE);
15016
15017       CloseDoor(DOOR_CLOSE_1);
15018     }
15019
15020     if (network.enabled)
15021       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15022     else
15023     {
15024       if (quick_quit)
15025         FadeSkipNextFadeIn();
15026
15027       SetGameStatus(GAME_MODE_MAIN);
15028
15029       DrawMainMenu();
15030     }
15031   }
15032   else          // continue playing the game
15033   {
15034     if (tape.playing && tape.deactivate_display)
15035       TapeDeactivateDisplayOff(TRUE);
15036
15037     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15038
15039     if (tape.playing && tape.deactivate_display)
15040       TapeDeactivateDisplayOn();
15041   }
15042 }
15043
15044 void RequestQuitGame(boolean ask_if_really_quit)
15045 {
15046   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15047   boolean skip_request = game.all_players_gone || quick_quit;
15048
15049   RequestQuitGameExt(skip_request, quick_quit,
15050                      "Do you really want to quit the game?");
15051 }
15052
15053 void RequestRestartGame(char *message)
15054 {
15055   game.restart_game_message = NULL;
15056
15057   boolean has_started_game = hasStartedNetworkGame();
15058   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15059
15060   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15061   {
15062     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15063   }
15064   else
15065   {
15066     SetGameStatus(GAME_MODE_MAIN);
15067
15068     DrawMainMenu();
15069   }
15070 }
15071
15072 void CheckGameOver(void)
15073 {
15074   static boolean last_game_over = FALSE;
15075   static int game_over_delay = 0;
15076   int game_over_delay_value = 50;
15077   boolean game_over = checkGameFailed();
15078
15079   // do not handle game over if request dialog is already active
15080   if (game.request_active)
15081     return;
15082
15083   // do not ask to play again if game was never actually played
15084   if (!game.GamePlayed)
15085     return;
15086
15087   if (!game_over)
15088   {
15089     last_game_over = FALSE;
15090     game_over_delay = game_over_delay_value;
15091
15092     return;
15093   }
15094
15095   if (game_over_delay > 0)
15096   {
15097     game_over_delay--;
15098
15099     return;
15100   }
15101
15102   if (last_game_over != game_over)
15103     game.restart_game_message = (hasStartedNetworkGame() ?
15104                                  "Game over! Play it again?" :
15105                                  "Game over!");
15106
15107   last_game_over = game_over;
15108 }
15109
15110 boolean checkGameSolved(void)
15111 {
15112   // set for all game engines if level was solved
15113   return game.LevelSolved_GameEnd;
15114 }
15115
15116 boolean checkGameFailed(void)
15117 {
15118   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15119     return (game_em.game_over && !game_em.level_solved);
15120   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15121     return (game_sp.game_over && !game_sp.level_solved);
15122   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15123     return (game_mm.game_over && !game_mm.level_solved);
15124   else                          // GAME_ENGINE_TYPE_RND
15125     return (game.GameOver && !game.LevelSolved);
15126 }
15127
15128 boolean checkGameEnded(void)
15129 {
15130   return (checkGameSolved() || checkGameFailed());
15131 }
15132
15133
15134 // ----------------------------------------------------------------------------
15135 // random generator functions
15136 // ----------------------------------------------------------------------------
15137
15138 unsigned int InitEngineRandom_RND(int seed)
15139 {
15140   game.num_random_calls = 0;
15141
15142   return InitEngineRandom(seed);
15143 }
15144
15145 unsigned int RND(int max)
15146 {
15147   if (max > 0)
15148   {
15149     game.num_random_calls++;
15150
15151     return GetEngineRandom(max);
15152   }
15153
15154   return 0;
15155 }
15156
15157
15158 // ----------------------------------------------------------------------------
15159 // game engine snapshot handling functions
15160 // ----------------------------------------------------------------------------
15161
15162 struct EngineSnapshotInfo
15163 {
15164   // runtime values for custom element collect score
15165   int collect_score[NUM_CUSTOM_ELEMENTS];
15166
15167   // runtime values for group element choice position
15168   int choice_pos[NUM_GROUP_ELEMENTS];
15169
15170   // runtime values for belt position animations
15171   int belt_graphic[4][NUM_BELT_PARTS];
15172   int belt_anim_mode[4][NUM_BELT_PARTS];
15173 };
15174
15175 static struct EngineSnapshotInfo engine_snapshot_rnd;
15176 static char *snapshot_level_identifier = NULL;
15177 static int snapshot_level_nr = -1;
15178
15179 static void SaveEngineSnapshotValues_RND(void)
15180 {
15181   static int belt_base_active_element[4] =
15182   {
15183     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15184     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15185     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15186     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15187   };
15188   int i, j;
15189
15190   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15191   {
15192     int element = EL_CUSTOM_START + i;
15193
15194     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15195   }
15196
15197   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15198   {
15199     int element = EL_GROUP_START + i;
15200
15201     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15202   }
15203
15204   for (i = 0; i < 4; i++)
15205   {
15206     for (j = 0; j < NUM_BELT_PARTS; j++)
15207     {
15208       int element = belt_base_active_element[i] + j;
15209       int graphic = el2img(element);
15210       int anim_mode = graphic_info[graphic].anim_mode;
15211
15212       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15213       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15214     }
15215   }
15216 }
15217
15218 static void LoadEngineSnapshotValues_RND(void)
15219 {
15220   unsigned int num_random_calls = game.num_random_calls;
15221   int i, j;
15222
15223   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15224   {
15225     int element = EL_CUSTOM_START + i;
15226
15227     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15228   }
15229
15230   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15231   {
15232     int element = EL_GROUP_START + i;
15233
15234     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15235   }
15236
15237   for (i = 0; i < 4; i++)
15238   {
15239     for (j = 0; j < NUM_BELT_PARTS; j++)
15240     {
15241       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15242       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15243
15244       graphic_info[graphic].anim_mode = anim_mode;
15245     }
15246   }
15247
15248   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15249   {
15250     InitRND(tape.random_seed);
15251     for (i = 0; i < num_random_calls; i++)
15252       RND(1);
15253   }
15254
15255   if (game.num_random_calls != num_random_calls)
15256   {
15257     Error(ERR_INFO, "number of random calls out of sync");
15258     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15259     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15260     Error(ERR_EXIT, "this should not happen -- please debug");
15261   }
15262 }
15263
15264 void FreeEngineSnapshotSingle(void)
15265 {
15266   FreeSnapshotSingle();
15267
15268   setString(&snapshot_level_identifier, NULL);
15269   snapshot_level_nr = -1;
15270 }
15271
15272 void FreeEngineSnapshotList(void)
15273 {
15274   FreeSnapshotList();
15275 }
15276
15277 static ListNode *SaveEngineSnapshotBuffers(void)
15278 {
15279   ListNode *buffers = NULL;
15280
15281   // copy some special values to a structure better suited for the snapshot
15282
15283   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15284     SaveEngineSnapshotValues_RND();
15285   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15286     SaveEngineSnapshotValues_EM();
15287   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15288     SaveEngineSnapshotValues_SP(&buffers);
15289   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15290     SaveEngineSnapshotValues_MM(&buffers);
15291
15292   // save values stored in special snapshot structure
15293
15294   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15295     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15296   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15297     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15298   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15299     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15300   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15301     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15302
15303   // save further RND engine values
15304
15305   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15306   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15307   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15308
15309   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15310   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15311   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15312   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15313   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15314
15315   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15316   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15317   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15318
15319   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15320
15321   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15322   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15323
15324   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15325   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15326   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15327   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15328   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15329   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15330   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15331   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15332   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15333   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15334   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15335   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15336   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15337   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15338   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15339   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15340   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15341   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15342
15343   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15344   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15345
15346   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15347   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15348   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15349
15350   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15351   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15352
15353   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15354   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15355   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15356   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15357   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15358
15359   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15360   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15361
15362 #if 0
15363   ListNode *node = engine_snapshot_list_rnd;
15364   int num_bytes = 0;
15365
15366   while (node != NULL)
15367   {
15368     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15369
15370     node = node->next;
15371   }
15372
15373   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15374 #endif
15375
15376   return buffers;
15377 }
15378
15379 void SaveEngineSnapshotSingle(void)
15380 {
15381   ListNode *buffers = SaveEngineSnapshotBuffers();
15382
15383   // finally save all snapshot buffers to single snapshot
15384   SaveSnapshotSingle(buffers);
15385
15386   // save level identification information
15387   setString(&snapshot_level_identifier, leveldir_current->identifier);
15388   snapshot_level_nr = level_nr;
15389 }
15390
15391 boolean CheckSaveEngineSnapshotToList(void)
15392 {
15393   boolean save_snapshot =
15394     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15395      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15396       game.snapshot.changed_action) ||
15397      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15398       game.snapshot.collected_item));
15399
15400   game.snapshot.changed_action = FALSE;
15401   game.snapshot.collected_item = FALSE;
15402   game.snapshot.save_snapshot = save_snapshot;
15403
15404   return save_snapshot;
15405 }
15406
15407 void SaveEngineSnapshotToList(void)
15408 {
15409   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15410       tape.quick_resume)
15411     return;
15412
15413   ListNode *buffers = SaveEngineSnapshotBuffers();
15414
15415   // finally save all snapshot buffers to snapshot list
15416   SaveSnapshotToList(buffers);
15417 }
15418
15419 void SaveEngineSnapshotToListInitial(void)
15420 {
15421   FreeEngineSnapshotList();
15422
15423   SaveEngineSnapshotToList();
15424 }
15425
15426 static void LoadEngineSnapshotValues(void)
15427 {
15428   // restore special values from snapshot structure
15429
15430   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15431     LoadEngineSnapshotValues_RND();
15432   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15433     LoadEngineSnapshotValues_EM();
15434   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15435     LoadEngineSnapshotValues_SP();
15436   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15437     LoadEngineSnapshotValues_MM();
15438 }
15439
15440 void LoadEngineSnapshotSingle(void)
15441 {
15442   LoadSnapshotSingle();
15443
15444   LoadEngineSnapshotValues();
15445 }
15446
15447 static void LoadEngineSnapshot_Undo(int steps)
15448 {
15449   LoadSnapshotFromList_Older(steps);
15450
15451   LoadEngineSnapshotValues();
15452 }
15453
15454 static void LoadEngineSnapshot_Redo(int steps)
15455 {
15456   LoadSnapshotFromList_Newer(steps);
15457
15458   LoadEngineSnapshotValues();
15459 }
15460
15461 boolean CheckEngineSnapshotSingle(void)
15462 {
15463   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15464           snapshot_level_nr == level_nr);
15465 }
15466
15467 boolean CheckEngineSnapshotList(void)
15468 {
15469   return CheckSnapshotList();
15470 }
15471
15472
15473 // ---------- new game button stuff -------------------------------------------
15474
15475 static struct
15476 {
15477   int graphic;
15478   struct XY *pos;
15479   int gadget_id;
15480   boolean *setup_value;
15481   boolean allowed_on_tape;
15482   boolean is_touch_button;
15483   char *infotext;
15484 } gamebutton_info[NUM_GAME_BUTTONS] =
15485 {
15486   {
15487     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15488     GAME_CTRL_ID_STOP,                          NULL,
15489     TRUE, FALSE,                                "stop game"
15490   },
15491   {
15492     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15493     GAME_CTRL_ID_PAUSE,                         NULL,
15494     TRUE, FALSE,                                "pause game"
15495   },
15496   {
15497     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15498     GAME_CTRL_ID_PLAY,                          NULL,
15499     TRUE, FALSE,                                "play game"
15500   },
15501   {
15502     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15503     GAME_CTRL_ID_UNDO,                          NULL,
15504     TRUE, FALSE,                                "undo step"
15505   },
15506   {
15507     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15508     GAME_CTRL_ID_REDO,                          NULL,
15509     TRUE, FALSE,                                "redo step"
15510   },
15511   {
15512     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15513     GAME_CTRL_ID_SAVE,                          NULL,
15514     TRUE, FALSE,                                "save game"
15515   },
15516   {
15517     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15518     GAME_CTRL_ID_PAUSE2,                        NULL,
15519     TRUE, FALSE,                                "pause game"
15520   },
15521   {
15522     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15523     GAME_CTRL_ID_LOAD,                          NULL,
15524     TRUE, FALSE,                                "load game"
15525   },
15526   {
15527     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15528     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15529     FALSE, FALSE,                               "stop game"
15530   },
15531   {
15532     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15533     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15534     FALSE, FALSE,                               "pause game"
15535   },
15536   {
15537     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15538     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15539     FALSE, FALSE,                               "play game"
15540   },
15541   {
15542     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15543     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15544     FALSE, TRUE,                                "stop game"
15545   },
15546   {
15547     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15548     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15549     FALSE, TRUE,                                "pause game"
15550   },
15551   {
15552     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15553     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15554     TRUE, FALSE,                                "background music on/off"
15555   },
15556   {
15557     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15558     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15559     TRUE, FALSE,                                "sound loops on/off"
15560   },
15561   {
15562     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15563     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15564     TRUE, FALSE,                                "normal sounds on/off"
15565   },
15566   {
15567     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15568     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15569     FALSE, FALSE,                               "background music on/off"
15570   },
15571   {
15572     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15573     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15574     FALSE, FALSE,                               "sound loops on/off"
15575   },
15576   {
15577     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15578     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15579     FALSE, FALSE,                               "normal sounds on/off"
15580   }
15581 };
15582
15583 void CreateGameButtons(void)
15584 {
15585   int i;
15586
15587   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15588   {
15589     int graphic = gamebutton_info[i].graphic;
15590     struct GraphicInfo *gfx = &graphic_info[graphic];
15591     struct XY *pos = gamebutton_info[i].pos;
15592     struct GadgetInfo *gi;
15593     int button_type;
15594     boolean checked;
15595     unsigned int event_mask;
15596     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15597     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15598     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15599     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15600     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15601     int gd_x   = gfx->src_x;
15602     int gd_y   = gfx->src_y;
15603     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15604     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15605     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15606     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15607     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15608     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15609     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15610     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15611     int id = i;
15612
15613     if (gfx->bitmap == NULL)
15614     {
15615       game_gadget[id] = NULL;
15616
15617       continue;
15618     }
15619
15620     if (id == GAME_CTRL_ID_STOP ||
15621         id == GAME_CTRL_ID_PANEL_STOP ||
15622         id == GAME_CTRL_ID_TOUCH_STOP ||
15623         id == GAME_CTRL_ID_PLAY ||
15624         id == GAME_CTRL_ID_PANEL_PLAY ||
15625         id == GAME_CTRL_ID_SAVE ||
15626         id == GAME_CTRL_ID_LOAD)
15627     {
15628       button_type = GD_TYPE_NORMAL_BUTTON;
15629       checked = FALSE;
15630       event_mask = GD_EVENT_RELEASED;
15631     }
15632     else if (id == GAME_CTRL_ID_UNDO ||
15633              id == GAME_CTRL_ID_REDO)
15634     {
15635       button_type = GD_TYPE_NORMAL_BUTTON;
15636       checked = FALSE;
15637       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15638     }
15639     else
15640     {
15641       button_type = GD_TYPE_CHECK_BUTTON;
15642       checked = (gamebutton_info[i].setup_value != NULL ?
15643                  *gamebutton_info[i].setup_value : FALSE);
15644       event_mask = GD_EVENT_PRESSED;
15645     }
15646
15647     gi = CreateGadget(GDI_CUSTOM_ID, id,
15648                       GDI_IMAGE_ID, graphic,
15649                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15650                       GDI_X, base_x + x,
15651                       GDI_Y, base_y + y,
15652                       GDI_WIDTH, gfx->width,
15653                       GDI_HEIGHT, gfx->height,
15654                       GDI_TYPE, button_type,
15655                       GDI_STATE, GD_BUTTON_UNPRESSED,
15656                       GDI_CHECKED, checked,
15657                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15658                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15659                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15660                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15661                       GDI_DIRECT_DRAW, FALSE,
15662                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
15663                       GDI_EVENT_MASK, event_mask,
15664                       GDI_CALLBACK_ACTION, HandleGameButtons,
15665                       GDI_END);
15666
15667     if (gi == NULL)
15668       Error(ERR_EXIT, "cannot create gadget");
15669
15670     game_gadget[id] = gi;
15671   }
15672 }
15673
15674 void FreeGameButtons(void)
15675 {
15676   int i;
15677
15678   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15679     FreeGadget(game_gadget[i]);
15680 }
15681
15682 static void UnmapGameButtonsAtSamePosition(int id)
15683 {
15684   int i;
15685
15686   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15687     if (i != id &&
15688         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15689         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15690       UnmapGadget(game_gadget[i]);
15691 }
15692
15693 static void UnmapGameButtonsAtSamePosition_All(void)
15694 {
15695   if (setup.show_snapshot_buttons)
15696   {
15697     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15698     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15699     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15700   }
15701   else
15702   {
15703     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15704     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15705     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15706
15707     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15708     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15709     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15710   }
15711 }
15712
15713 static void MapGameButtonsAtSamePosition(int id)
15714 {
15715   int i;
15716
15717   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15718     if (i != id &&
15719         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15720         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15721       MapGadget(game_gadget[i]);
15722
15723   UnmapGameButtonsAtSamePosition_All();
15724 }
15725
15726 void MapUndoRedoButtons(void)
15727 {
15728   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15729   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15730
15731   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15732   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15733 }
15734
15735 void UnmapUndoRedoButtons(void)
15736 {
15737   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15738   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15739
15740   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15741   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15742 }
15743
15744 void ModifyPauseButtons(void)
15745 {
15746   static int ids[] =
15747   {
15748     GAME_CTRL_ID_PAUSE,
15749     GAME_CTRL_ID_PAUSE2,
15750     GAME_CTRL_ID_PANEL_PAUSE,
15751     GAME_CTRL_ID_TOUCH_PAUSE,
15752     -1
15753   };
15754   int i;
15755
15756   for (i = 0; ids[i] > -1; i++)
15757     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
15758 }
15759
15760 static void MapGameButtonsExt(boolean on_tape)
15761 {
15762   int i;
15763
15764   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15765     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15766         i != GAME_CTRL_ID_UNDO &&
15767         i != GAME_CTRL_ID_REDO)
15768       MapGadget(game_gadget[i]);
15769
15770   UnmapGameButtonsAtSamePosition_All();
15771
15772   RedrawGameButtons();
15773 }
15774
15775 static void UnmapGameButtonsExt(boolean on_tape)
15776 {
15777   int i;
15778
15779   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15780     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15781       UnmapGadget(game_gadget[i]);
15782 }
15783
15784 static void RedrawGameButtonsExt(boolean on_tape)
15785 {
15786   int i;
15787
15788   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15789     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15790       RedrawGadget(game_gadget[i]);
15791 }
15792
15793 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15794 {
15795   if (gi == NULL)
15796     return;
15797
15798   gi->checked = state;
15799 }
15800
15801 static void RedrawSoundButtonGadget(int id)
15802 {
15803   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15804              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15805              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15806              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15807              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15808              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15809              id);
15810
15811   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15812   RedrawGadget(game_gadget[id2]);
15813 }
15814
15815 void MapGameButtons(void)
15816 {
15817   MapGameButtonsExt(FALSE);
15818 }
15819
15820 void UnmapGameButtons(void)
15821 {
15822   UnmapGameButtonsExt(FALSE);
15823 }
15824
15825 void RedrawGameButtons(void)
15826 {
15827   RedrawGameButtonsExt(FALSE);
15828 }
15829
15830 void MapGameButtonsOnTape(void)
15831 {
15832   MapGameButtonsExt(TRUE);
15833 }
15834
15835 void UnmapGameButtonsOnTape(void)
15836 {
15837   UnmapGameButtonsExt(TRUE);
15838 }
15839
15840 void RedrawGameButtonsOnTape(void)
15841 {
15842   RedrawGameButtonsExt(TRUE);
15843 }
15844
15845 static void GameUndoRedoExt(void)
15846 {
15847   ClearPlayerAction();
15848
15849   tape.pausing = TRUE;
15850
15851   RedrawPlayfield();
15852   UpdateAndDisplayGameControlValues();
15853
15854   DrawCompleteVideoDisplay();
15855   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15856   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15857   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15858
15859   BackToFront();
15860 }
15861
15862 static void GameUndo(int steps)
15863 {
15864   if (!CheckEngineSnapshotList())
15865     return;
15866
15867   LoadEngineSnapshot_Undo(steps);
15868
15869   GameUndoRedoExt();
15870 }
15871
15872 static void GameRedo(int steps)
15873 {
15874   if (!CheckEngineSnapshotList())
15875     return;
15876
15877   LoadEngineSnapshot_Redo(steps);
15878
15879   GameUndoRedoExt();
15880 }
15881
15882 static void HandleGameButtonsExt(int id, int button)
15883 {
15884   static boolean game_undo_executed = FALSE;
15885   int steps = BUTTON_STEPSIZE(button);
15886   boolean handle_game_buttons =
15887     (game_status == GAME_MODE_PLAYING ||
15888      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15889
15890   if (!handle_game_buttons)
15891     return;
15892
15893   switch (id)
15894   {
15895     case GAME_CTRL_ID_STOP:
15896     case GAME_CTRL_ID_PANEL_STOP:
15897     case GAME_CTRL_ID_TOUCH_STOP:
15898       if (game_status == GAME_MODE_MAIN)
15899         break;
15900
15901       if (tape.playing)
15902         TapeStop();
15903       else
15904         RequestQuitGame(TRUE);
15905
15906       break;
15907
15908     case GAME_CTRL_ID_PAUSE:
15909     case GAME_CTRL_ID_PAUSE2:
15910     case GAME_CTRL_ID_PANEL_PAUSE:
15911     case GAME_CTRL_ID_TOUCH_PAUSE:
15912       if (network.enabled && game_status == GAME_MODE_PLAYING)
15913       {
15914         if (tape.pausing)
15915           SendToServer_ContinuePlaying();
15916         else
15917           SendToServer_PausePlaying();
15918       }
15919       else
15920         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15921
15922       game_undo_executed = FALSE;
15923
15924       break;
15925
15926     case GAME_CTRL_ID_PLAY:
15927     case GAME_CTRL_ID_PANEL_PLAY:
15928       if (game_status == GAME_MODE_MAIN)
15929       {
15930         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15931       }
15932       else if (tape.pausing)
15933       {
15934         if (network.enabled)
15935           SendToServer_ContinuePlaying();
15936         else
15937           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15938       }
15939       break;
15940
15941     case GAME_CTRL_ID_UNDO:
15942       // Important: When using "save snapshot when collecting an item" mode,
15943       // load last (current) snapshot for first "undo" after pressing "pause"
15944       // (else the last-but-one snapshot would be loaded, because the snapshot
15945       // pointer already points to the last snapshot when pressing "pause",
15946       // which is fine for "every step/move" mode, but not for "every collect")
15947       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15948           !game_undo_executed)
15949         steps--;
15950
15951       game_undo_executed = TRUE;
15952
15953       GameUndo(steps);
15954       break;
15955
15956     case GAME_CTRL_ID_REDO:
15957       GameRedo(steps);
15958       break;
15959
15960     case GAME_CTRL_ID_SAVE:
15961       TapeQuickSave();
15962       break;
15963
15964     case GAME_CTRL_ID_LOAD:
15965       TapeQuickLoad();
15966       break;
15967
15968     case SOUND_CTRL_ID_MUSIC:
15969     case SOUND_CTRL_ID_PANEL_MUSIC:
15970       if (setup.sound_music)
15971       { 
15972         setup.sound_music = FALSE;
15973
15974         FadeMusic();
15975       }
15976       else if (audio.music_available)
15977       { 
15978         setup.sound = setup.sound_music = TRUE;
15979
15980         SetAudioMode(setup.sound);
15981
15982         if (game_status == GAME_MODE_PLAYING)
15983           PlayLevelMusic();
15984       }
15985
15986       RedrawSoundButtonGadget(id);
15987
15988       break;
15989
15990     case SOUND_CTRL_ID_LOOPS:
15991     case SOUND_CTRL_ID_PANEL_LOOPS:
15992       if (setup.sound_loops)
15993         setup.sound_loops = FALSE;
15994       else if (audio.loops_available)
15995       {
15996         setup.sound = setup.sound_loops = TRUE;
15997
15998         SetAudioMode(setup.sound);
15999       }
16000
16001       RedrawSoundButtonGadget(id);
16002
16003       break;
16004
16005     case SOUND_CTRL_ID_SIMPLE:
16006     case SOUND_CTRL_ID_PANEL_SIMPLE:
16007       if (setup.sound_simple)
16008         setup.sound_simple = FALSE;
16009       else if (audio.sound_available)
16010       {
16011         setup.sound = setup.sound_simple = TRUE;
16012
16013         SetAudioMode(setup.sound);
16014       }
16015
16016       RedrawSoundButtonGadget(id);
16017
16018       break;
16019
16020     default:
16021       break;
16022   }
16023 }
16024
16025 static void HandleGameButtons(struct GadgetInfo *gi)
16026 {
16027   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16028 }
16029
16030 void HandleSoundButtonKeys(Key key)
16031 {
16032   if (key == setup.shortcut.sound_simple)
16033     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16034   else if (key == setup.shortcut.sound_loops)
16035     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16036   else if (key == setup.shortcut.sound_music)
16037     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16038 }