removed preprocessor definition for network mode that is always true now
[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()             (local_player->LevelSolved_PanelOff)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 /* game panel display and control definitions */
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 /* values for delayed check of falling and moving elements and for collision */
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 /* values for initial player move delay (initial delay counter value) */
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 /* values for player movement speed (which is in fact a delay value) */
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 /* values for scroll positions */
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1006
1007 /* game button identifiers */
1008 #define GAME_CTRL_ID_STOP               0
1009 #define GAME_CTRL_ID_PAUSE              1
1010 #define GAME_CTRL_ID_PLAY               2
1011 #define GAME_CTRL_ID_UNDO               3
1012 #define GAME_CTRL_ID_REDO               4
1013 #define GAME_CTRL_ID_SAVE               5
1014 #define GAME_CTRL_ID_PAUSE2             6
1015 #define GAME_CTRL_ID_LOAD               7
1016 #define GAME_CTRL_ID_PANEL_STOP         8
1017 #define GAME_CTRL_ID_PANEL_PAUSE        9
1018 #define GAME_CTRL_ID_PANEL_PLAY         10
1019 #define SOUND_CTRL_ID_MUSIC             11
1020 #define SOUND_CTRL_ID_LOOPS             12
1021 #define SOUND_CTRL_ID_SIMPLE            13
1022 #define SOUND_CTRL_ID_PANEL_MUSIC       14
1023 #define SOUND_CTRL_ID_PANEL_LOOPS       15
1024 #define SOUND_CTRL_ID_PANEL_SIMPLE      16
1025
1026 #define NUM_GAME_BUTTONS                17
1027
1028
1029 /* forward declaration for internal use */
1030
1031 static void CreateField(int, int, int);
1032
1033 static void ResetGfxAnimation(int, int);
1034
1035 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1036 static void AdvanceFrameAndPlayerCounters(int);
1037
1038 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1039 static boolean MovePlayer(struct PlayerInfo *, int, int);
1040 static void ScrollPlayer(struct PlayerInfo *, int);
1041 static void ScrollScreen(struct PlayerInfo *, int);
1042
1043 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1044 static boolean DigFieldByCE(int, int, int);
1045 static boolean SnapField(struct PlayerInfo *, int, int);
1046 static boolean DropElement(struct PlayerInfo *);
1047
1048 static void InitBeltMovement(void);
1049 static void CloseAllOpenTimegates(void);
1050 static void CheckGravityMovement(struct PlayerInfo *);
1051 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1052 static void KillPlayerUnlessEnemyProtected(int, int);
1053 static void KillPlayerUnlessExplosionProtected(int, int);
1054
1055 static void TestIfPlayerTouchesCustomElement(int, int);
1056 static void TestIfElementTouchesCustomElement(int, int);
1057 static void TestIfElementHitsCustomElement(int, int, int);
1058
1059 static void HandleElementChange(int, int, int);
1060 static void ExecuteCustomElementAction(int, int, int, int);
1061 static boolean ChangeElement(int, int, int, int);
1062
1063 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1064 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1066 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1067         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1068 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1069         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1070 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1072
1073 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1074 #define CheckElementChange(x, y, e, te, ev)                             \
1075         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1076 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1077         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1078 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1079         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1080
1081 static void PlayLevelSound(int, int, int);
1082 static void PlayLevelSoundNearest(int, int, int);
1083 static void PlayLevelSoundAction(int, int, int);
1084 static void PlayLevelSoundElementAction(int, int, int, int);
1085 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1086 static void PlayLevelSoundActionIfLoop(int, int, int);
1087 static void StopLevelSoundActionIfLoop(int, int, int);
1088 static void PlayLevelMusic();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!network.enabled || player->connected_network)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 void InitGame()
3277 {
3278   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280   int fade_mask = REDRAW_FIELD;
3281
3282   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3283   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3284   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3285   int initial_move_dir = MV_DOWN;
3286   int i, j, x, y;
3287
3288   // required here to update video display before fading (FIX THIS)
3289   DrawMaskedBorder(REDRAW_DOOR_2);
3290
3291   if (!game.restart_level)
3292     CloseDoor(DOOR_CLOSE_1);
3293
3294   SetGameStatus(GAME_MODE_PLAYING);
3295
3296   if (level_editor_test_game)
3297     FadeSkipNextFadeIn();
3298   else
3299     FadeSetEnterScreen();
3300
3301   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302     fade_mask = REDRAW_ALL;
3303
3304   FadeLevelSoundsAndMusic();
3305
3306   ExpireSoundLoops(TRUE);
3307
3308   if (!level_editor_test_game)
3309     FadeOut(fade_mask);
3310
3311   /* needed if different viewport properties defined for playing */
3312   ChangeViewportPropertiesIfNeeded();
3313
3314   ClearField();
3315
3316   DrawCompleteVideoDisplay();
3317
3318   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3319
3320   InitGameEngine();
3321   InitGameControlValues();
3322
3323   /* don't play tapes over network */
3324   network_playing = (network.enabled && !tape.playing);
3325
3326   for (i = 0; i < MAX_PLAYERS; i++)
3327   {
3328     struct PlayerInfo *player = &stored_player[i];
3329
3330     player->index_nr = i;
3331     player->index_bit = (1 << i);
3332     player->element_nr = EL_PLAYER_1 + i;
3333
3334     player->present = FALSE;
3335     player->active = FALSE;
3336     player->mapped = FALSE;
3337
3338     player->killed = FALSE;
3339     player->reanimated = FALSE;
3340
3341     player->action = 0;
3342     player->effective_action = 0;
3343     player->programmed_action = 0;
3344
3345     player->mouse_action.lx = 0;
3346     player->mouse_action.ly = 0;
3347     player->mouse_action.button = 0;
3348     player->mouse_action.button_hint = 0;
3349
3350     player->effective_mouse_action.lx = 0;
3351     player->effective_mouse_action.ly = 0;
3352     player->effective_mouse_action.button = 0;
3353     player->effective_mouse_action.button_hint = 0;
3354
3355     player->score = 0;
3356     player->score_final = 0;
3357
3358     player->health = MAX_HEALTH;
3359     player->health_final = MAX_HEALTH;
3360
3361     player->gems_still_needed = level.gems_needed;
3362     player->sokobanfields_still_needed = 0;
3363     player->lights_still_needed = 0;
3364     player->friends_still_needed = 0;
3365
3366     for (j = 0; j < MAX_NUM_KEYS; j++)
3367       player->key[j] = FALSE;
3368
3369     player->num_white_keys = 0;
3370
3371     player->dynabomb_count = 0;
3372     player->dynabomb_size = 1;
3373     player->dynabombs_left = 0;
3374     player->dynabomb_xl = FALSE;
3375
3376     player->MovDir = initial_move_dir;
3377     player->MovPos = 0;
3378     player->GfxPos = 0;
3379     player->GfxDir = initial_move_dir;
3380     player->GfxAction = ACTION_DEFAULT;
3381     player->Frame = 0;
3382     player->StepFrame = 0;
3383
3384     player->initial_element = player->element_nr;
3385     player->artwork_element =
3386       (level.use_artwork_element[i] ? level.artwork_element[i] :
3387        player->element_nr);
3388     player->use_murphy = FALSE;
3389
3390     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3391     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3392
3393     player->gravity = level.initial_player_gravity[i];
3394
3395     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3396
3397     player->actual_frame_counter = 0;
3398
3399     player->step_counter = 0;
3400
3401     player->last_move_dir = initial_move_dir;
3402
3403     player->is_active = FALSE;
3404
3405     player->is_waiting = FALSE;
3406     player->is_moving = FALSE;
3407     player->is_auto_moving = FALSE;
3408     player->is_digging = FALSE;
3409     player->is_snapping = FALSE;
3410     player->is_collecting = FALSE;
3411     player->is_pushing = FALSE;
3412     player->is_switching = FALSE;
3413     player->is_dropping = FALSE;
3414     player->is_dropping_pressed = FALSE;
3415
3416     player->is_bored = FALSE;
3417     player->is_sleeping = FALSE;
3418
3419     player->was_waiting = TRUE;
3420     player->was_moving = FALSE;
3421     player->was_snapping = FALSE;
3422     player->was_dropping = FALSE;
3423
3424     player->force_dropping = FALSE;
3425
3426     player->frame_counter_bored = -1;
3427     player->frame_counter_sleeping = -1;
3428
3429     player->anim_delay_counter = 0;
3430     player->post_delay_counter = 0;
3431
3432     player->dir_waiting = initial_move_dir;
3433     player->action_waiting = ACTION_DEFAULT;
3434     player->last_action_waiting = ACTION_DEFAULT;
3435     player->special_action_bored = ACTION_DEFAULT;
3436     player->special_action_sleeping = ACTION_DEFAULT;
3437
3438     player->switch_x = -1;
3439     player->switch_y = -1;
3440
3441     player->drop_x = -1;
3442     player->drop_y = -1;
3443
3444     player->show_envelope = 0;
3445
3446     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3447
3448     player->push_delay       = -1;      /* initialized when pushing starts */
3449     player->push_delay_value = game.initial_push_delay_value;
3450
3451     player->drop_delay = 0;
3452     player->drop_pressed_delay = 0;
3453
3454     player->last_jx = -1;
3455     player->last_jy = -1;
3456     player->jx = -1;
3457     player->jy = -1;
3458
3459     player->shield_normal_time_left = 0;
3460     player->shield_deadly_time_left = 0;
3461
3462     player->inventory_infinite_element = EL_UNDEFINED;
3463     player->inventory_size = 0;
3464
3465     if (level.use_initial_inventory[i])
3466     {
3467       for (j = 0; j < level.initial_inventory_size[i]; j++)
3468       {
3469         int element = level.initial_inventory_content[i][j];
3470         int collect_count = element_info[element].collect_count_initial;
3471         int k;
3472
3473         if (!IS_CUSTOM_ELEMENT(element))
3474           collect_count = 1;
3475
3476         if (collect_count == 0)
3477           player->inventory_infinite_element = element;
3478         else
3479           for (k = 0; k < collect_count; k++)
3480             if (player->inventory_size < MAX_INVENTORY_SIZE)
3481               player->inventory_element[player->inventory_size++] = element;
3482       }
3483     }
3484
3485     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3486     SnapField(player, 0, 0);
3487
3488     player->LevelSolved = FALSE;
3489     player->GameOver = FALSE;
3490
3491     player->LevelSolved_GameWon = FALSE;
3492     player->LevelSolved_GameEnd = FALSE;
3493     player->LevelSolved_PanelOff = FALSE;
3494     player->LevelSolved_SaveTape = FALSE;
3495     player->LevelSolved_SaveScore = FALSE;
3496
3497     player->LevelSolved_CountingTime = 0;
3498     player->LevelSolved_CountingScore = 0;
3499     player->LevelSolved_CountingHealth = 0;
3500
3501     map_player_action[i] = i;
3502   }
3503
3504   network_player_action_received = FALSE;
3505
3506   /* initial null action */
3507   if (network_playing)
3508     SendToServer_MovePlayer(MV_NONE);
3509
3510   ZX = ZY = -1;
3511   ExitX = ExitY = -1;
3512
3513   FrameCounter = 0;
3514   TimeFrames = 0;
3515   TimePlayed = 0;
3516   TimeLeft = level.time;
3517   TapeTime = 0;
3518
3519   ScreenMovDir = MV_NONE;
3520   ScreenMovPos = 0;
3521   ScreenGfxPos = 0;
3522
3523   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3524
3525   AllPlayersGone = FALSE;
3526
3527   game.no_time_limit = (level.time == 0);
3528
3529   game.yamyam_content_nr = 0;
3530   game.robot_wheel_active = FALSE;
3531   game.magic_wall_active = FALSE;
3532   game.magic_wall_time_left = 0;
3533   game.light_time_left = 0;
3534   game.timegate_time_left = 0;
3535   game.switchgate_pos = 0;
3536   game.wind_direction = level.wind_direction_initial;
3537
3538   game.lenses_time_left = 0;
3539   game.magnify_time_left = 0;
3540
3541   game.ball_state = level.ball_state_initial;
3542   game.ball_content_nr = 0;
3543
3544   game.envelope_active = FALSE;
3545
3546   for (i = 0; i < NUM_BELTS; i++)
3547   {
3548     game.belt_dir[i] = MV_NONE;
3549     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3550   }
3551
3552   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3553     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3554
3555 #if DEBUG_INIT_PLAYER
3556   if (options.debug)
3557   {
3558     printf("Player status at level initialization:\n");
3559
3560     for (i = 0; i < MAX_PLAYERS; i++)
3561     {
3562       struct PlayerInfo *player = &stored_player[i];
3563
3564       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3565              i + 1,
3566              player->present,
3567              player->connected,
3568              player->connected_locally,
3569              player->connected_network,
3570              player->active);
3571
3572       if (local_player == player)
3573         printf(" (local player)");
3574
3575       printf("\n");
3576     }
3577   }
3578 #endif
3579
3580   SCAN_PLAYFIELD(x, y)
3581   {
3582     Feld[x][y] = level.field[x][y];
3583     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3584     ChangeDelay[x][y] = 0;
3585     ChangePage[x][y] = -1;
3586     CustomValue[x][y] = 0;              /* initialized in InitField() */
3587     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3588     AmoebaNr[x][y] = 0;
3589     WasJustMoving[x][y] = 0;
3590     WasJustFalling[x][y] = 0;
3591     CheckCollision[x][y] = 0;
3592     CheckImpact[x][y] = 0;
3593     Stop[x][y] = FALSE;
3594     Pushed[x][y] = FALSE;
3595
3596     ChangeCount[x][y] = 0;
3597     ChangeEvent[x][y] = -1;
3598
3599     ExplodePhase[x][y] = 0;
3600     ExplodeDelay[x][y] = 0;
3601     ExplodeField[x][y] = EX_TYPE_NONE;
3602
3603     RunnerVisit[x][y] = 0;
3604     PlayerVisit[x][y] = 0;
3605
3606     GfxFrame[x][y] = 0;
3607     GfxRandom[x][y] = INIT_GFX_RANDOM();
3608     GfxElement[x][y] = EL_UNDEFINED;
3609     GfxAction[x][y] = ACTION_DEFAULT;
3610     GfxDir[x][y] = MV_NONE;
3611     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3612   }
3613
3614   SCAN_PLAYFIELD(x, y)
3615   {
3616     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3617       emulate_bd = FALSE;
3618     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3619       emulate_sb = FALSE;
3620     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3621       emulate_sp = FALSE;
3622
3623     InitField(x, y, TRUE);
3624
3625     ResetGfxAnimation(x, y);
3626   }
3627
3628   InitBeltMovement();
3629
3630   for (i = 0; i < MAX_PLAYERS; i++)
3631   {
3632     struct PlayerInfo *player = &stored_player[i];
3633
3634     /* set number of special actions for bored and sleeping animation */
3635     player->num_special_action_bored =
3636       get_num_special_action(player->artwork_element,
3637                              ACTION_BORING_1, ACTION_BORING_LAST);
3638     player->num_special_action_sleeping =
3639       get_num_special_action(player->artwork_element,
3640                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3641   }
3642
3643   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3644                     emulate_sb ? EMU_SOKOBAN :
3645                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3646
3647   /* initialize type of slippery elements */
3648   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3649   {
3650     if (!IS_CUSTOM_ELEMENT(i))
3651     {
3652       /* default: elements slip down either to the left or right randomly */
3653       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3654
3655       /* SP style elements prefer to slip down on the left side */
3656       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3657         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3658
3659       /* BD style elements prefer to slip down on the left side */
3660       if (game.emulation == EMU_BOULDERDASH)
3661         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3662     }
3663   }
3664
3665   /* initialize explosion and ignition delay */
3666   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3667   {
3668     if (!IS_CUSTOM_ELEMENT(i))
3669     {
3670       int num_phase = 8;
3671       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3672                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3673                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3674       int last_phase = (num_phase + 1) * delay;
3675       int half_phase = (num_phase / 2) * delay;
3676
3677       element_info[i].explosion_delay = last_phase - 1;
3678       element_info[i].ignition_delay = half_phase;
3679
3680       if (i == EL_BLACK_ORB)
3681         element_info[i].ignition_delay = 1;
3682     }
3683   }
3684
3685   /* correct non-moving belts to start moving left */
3686   for (i = 0; i < NUM_BELTS; i++)
3687     if (game.belt_dir[i] == MV_NONE)
3688       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3689
3690 #if USE_NEW_PLAYER_ASSIGNMENTS
3691   for (i = 0; i < MAX_PLAYERS; i++)
3692   {
3693     stored_player[i].connected = FALSE;
3694
3695     /* in network game mode, the local player might not be the first player */
3696     if (stored_player[i].connected_locally)
3697       local_player = &stored_player[i];
3698   }
3699
3700   if (!network.enabled)
3701     local_player->connected = TRUE;
3702
3703   if (tape.playing)
3704   {
3705     for (i = 0; i < MAX_PLAYERS; i++)
3706       stored_player[i].connected = tape.player_participates[i];
3707   }
3708   else if (network.enabled)
3709   {
3710     /* add team mode players connected over the network (needed for correct
3711        assignment of player figures from level to locally playing players) */
3712
3713     for (i = 0; i < MAX_PLAYERS; i++)
3714       if (stored_player[i].connected_network)
3715         stored_player[i].connected = TRUE;
3716   }
3717   else if (game.team_mode)
3718   {
3719     /* try to guess locally connected team mode players (needed for correct
3720        assignment of player figures from level to locally playing players) */
3721
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723       if (setup.input[i].use_joystick ||
3724           setup.input[i].key.left != KSYM_UNDEFINED)
3725         stored_player[i].connected = TRUE;
3726   }
3727
3728 #if DEBUG_INIT_PLAYER
3729   if (options.debug)
3730   {
3731     printf("Player status after level initialization:\n");
3732
3733     for (i = 0; i < MAX_PLAYERS; i++)
3734     {
3735       struct PlayerInfo *player = &stored_player[i];
3736
3737       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3738              i + 1,
3739              player->present,
3740              player->connected,
3741              player->connected_locally,
3742              player->connected_network,
3743              player->active);
3744
3745       if (local_player == player)
3746         printf(" (local player)");
3747
3748       printf("\n");
3749     }
3750   }
3751 #endif
3752
3753 #if DEBUG_INIT_PLAYER
3754   if (options.debug)
3755     printf("Reassigning players ...\n");
3756 #endif
3757
3758   /* check if any connected player was not found in playfield */
3759   for (i = 0; i < MAX_PLAYERS; i++)
3760   {
3761     struct PlayerInfo *player = &stored_player[i];
3762
3763     if (player->connected && !player->present)
3764     {
3765       struct PlayerInfo *field_player = NULL;
3766
3767 #if DEBUG_INIT_PLAYER
3768       if (options.debug)
3769         printf("- looking for field player for player %d ...\n", i + 1);
3770 #endif
3771
3772       /* assign first free player found that is present in the playfield */
3773
3774       /* first try: look for unmapped playfield player that is not connected */
3775       for (j = 0; j < MAX_PLAYERS; j++)
3776         if (field_player == NULL &&
3777             stored_player[j].present &&
3778             !stored_player[j].mapped &&
3779             !stored_player[j].connected)
3780           field_player = &stored_player[j];
3781
3782       /* second try: look for *any* unmapped playfield player */
3783       for (j = 0; j < MAX_PLAYERS; j++)
3784         if (field_player == NULL &&
3785             stored_player[j].present &&
3786             !stored_player[j].mapped)
3787           field_player = &stored_player[j];
3788
3789       if (field_player != NULL)
3790       {
3791         int jx = field_player->jx, jy = field_player->jy;
3792
3793 #if DEBUG_INIT_PLAYER
3794         if (options.debug)
3795           printf("- found player %d\n", field_player->index_nr + 1);
3796 #endif
3797
3798         player->present = FALSE;
3799         player->active = FALSE;
3800
3801         field_player->present = TRUE;
3802         field_player->active = TRUE;
3803
3804         /*
3805         player->initial_element = field_player->initial_element;
3806         player->artwork_element = field_player->artwork_element;
3807
3808         player->block_last_field       = field_player->block_last_field;
3809         player->block_delay_adjustment = field_player->block_delay_adjustment;
3810         */
3811
3812         StorePlayer[jx][jy] = field_player->element_nr;
3813
3814         field_player->jx = field_player->last_jx = jx;
3815         field_player->jy = field_player->last_jy = jy;
3816
3817         if (local_player == player)
3818           local_player = field_player;
3819
3820         map_player_action[field_player->index_nr] = i;
3821
3822         field_player->mapped = TRUE;
3823
3824 #if DEBUG_INIT_PLAYER
3825         if (options.debug)
3826           printf("- map_player_action[%d] == %d\n",
3827                  field_player->index_nr + 1, i + 1);
3828 #endif
3829       }
3830     }
3831
3832     if (player->connected && player->present)
3833       player->mapped = TRUE;
3834   }
3835
3836 #if DEBUG_INIT_PLAYER
3837   if (options.debug)
3838   {
3839     printf("Player status after player assignment (first stage):\n");
3840
3841     for (i = 0; i < MAX_PLAYERS; i++)
3842     {
3843       struct PlayerInfo *player = &stored_player[i];
3844
3845       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3846              i + 1,
3847              player->present,
3848              player->connected,
3849              player->connected_locally,
3850              player->connected_network,
3851              player->active);
3852
3853       if (local_player == player)
3854         printf(" (local player)");
3855
3856       printf("\n");
3857     }
3858   }
3859 #endif
3860
3861 #else
3862
3863   /* check if any connected player was not found in playfield */
3864   for (i = 0; i < MAX_PLAYERS; i++)
3865   {
3866     struct PlayerInfo *player = &stored_player[i];
3867
3868     if (player->connected && !player->present)
3869     {
3870       for (j = 0; j < MAX_PLAYERS; j++)
3871       {
3872         struct PlayerInfo *field_player = &stored_player[j];
3873         int jx = field_player->jx, jy = field_player->jy;
3874
3875         /* assign first free player found that is present in the playfield */
3876         if (field_player->present && !field_player->connected)
3877         {
3878           player->present = TRUE;
3879           player->active = TRUE;
3880
3881           field_player->present = FALSE;
3882           field_player->active = FALSE;
3883
3884           player->initial_element = field_player->initial_element;
3885           player->artwork_element = field_player->artwork_element;
3886
3887           player->block_last_field       = field_player->block_last_field;
3888           player->block_delay_adjustment = field_player->block_delay_adjustment;
3889
3890           StorePlayer[jx][jy] = player->element_nr;
3891
3892           player->jx = player->last_jx = jx;
3893           player->jy = player->last_jy = jy;
3894
3895           break;
3896         }
3897       }
3898     }
3899   }
3900 #endif
3901
3902 #if 0
3903   printf("::: local_player->present == %d\n", local_player->present);
3904 #endif
3905
3906   /* set focus to local player for network games, else to all players */
3907   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908   game.centered_player_nr_next = game.centered_player_nr;
3909   game.set_centered_player = FALSE;
3910
3911   if (network_playing && tape.recording)
3912   {
3913     /* store client dependent player focus when recording network games */
3914     tape.centered_player_nr_next = game.centered_player_nr_next;
3915     tape.set_centered_player = TRUE;
3916   }
3917
3918   if (tape.playing)
3919   {
3920     /* when playing a tape, eliminate all players who do not participate */
3921
3922 #if USE_NEW_PLAYER_ASSIGNMENTS
3923
3924     if (!game.team_mode)
3925     {
3926       for (i = 0; i < MAX_PLAYERS; i++)
3927       {
3928         if (stored_player[i].active &&
3929             !tape.player_participates[map_player_action[i]])
3930         {
3931           struct PlayerInfo *player = &stored_player[i];
3932           int jx = player->jx, jy = player->jy;
3933
3934 #if DEBUG_INIT_PLAYER
3935           if (options.debug)
3936             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3937 #endif
3938
3939           player->active = FALSE;
3940           StorePlayer[jx][jy] = 0;
3941           Feld[jx][jy] = EL_EMPTY;
3942         }
3943       }
3944     }
3945
3946 #else
3947
3948     for (i = 0; i < MAX_PLAYERS; i++)
3949     {
3950       if (stored_player[i].active &&
3951           !tape.player_participates[i])
3952       {
3953         struct PlayerInfo *player = &stored_player[i];
3954         int jx = player->jx, jy = player->jy;
3955
3956         player->active = FALSE;
3957         StorePlayer[jx][jy] = 0;
3958         Feld[jx][jy] = EL_EMPTY;
3959       }
3960     }
3961 #endif
3962   }
3963   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3964   {
3965     /* when in single player mode, eliminate all but the first active player */
3966
3967     for (i = 0; i < MAX_PLAYERS; i++)
3968     {
3969       if (stored_player[i].active)
3970       {
3971         for (j = i + 1; j < MAX_PLAYERS; j++)
3972         {
3973           if (stored_player[j].active)
3974           {
3975             struct PlayerInfo *player = &stored_player[j];
3976             int jx = player->jx, jy = player->jy;
3977
3978             player->active = FALSE;
3979             player->present = FALSE;
3980
3981             StorePlayer[jx][jy] = 0;
3982             Feld[jx][jy] = EL_EMPTY;
3983           }
3984         }
3985       }
3986     }
3987   }
3988
3989   /* when recording the game, store which players take part in the game */
3990   if (tape.recording)
3991   {
3992 #if USE_NEW_PLAYER_ASSIGNMENTS
3993     for (i = 0; i < MAX_PLAYERS; i++)
3994       if (stored_player[i].connected)
3995         tape.player_participates[i] = TRUE;
3996 #else
3997     for (i = 0; i < MAX_PLAYERS; i++)
3998       if (stored_player[i].active)
3999         tape.player_participates[i] = TRUE;
4000 #endif
4001   }
4002
4003 #if DEBUG_INIT_PLAYER
4004   if (options.debug)
4005   {
4006     printf("Player status after player assignment (final stage):\n");
4007
4008     for (i = 0; i < MAX_PLAYERS; i++)
4009     {
4010       struct PlayerInfo *player = &stored_player[i];
4011
4012       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4013              i + 1,
4014              player->present,
4015              player->connected,
4016              player->connected_locally,
4017              player->connected_network,
4018              player->active);
4019
4020       if (local_player == player)
4021         printf(" (local player)");
4022
4023       printf("\n");
4024     }
4025   }
4026 #endif
4027
4028   if (BorderElement == EL_EMPTY)
4029   {
4030     SBX_Left = 0;
4031     SBX_Right = lev_fieldx - SCR_FIELDX;
4032     SBY_Upper = 0;
4033     SBY_Lower = lev_fieldy - SCR_FIELDY;
4034   }
4035   else
4036   {
4037     SBX_Left = -1;
4038     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4039     SBY_Upper = -1;
4040     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4041   }
4042
4043   if (full_lev_fieldx <= SCR_FIELDX)
4044     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4045   if (full_lev_fieldy <= SCR_FIELDY)
4046     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4047
4048   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4049     SBX_Left--;
4050   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4051     SBY_Upper--;
4052
4053   /* if local player not found, look for custom element that might create
4054      the player (make some assumptions about the right custom element) */
4055   if (!local_player->present)
4056   {
4057     int start_x = 0, start_y = 0;
4058     int found_rating = 0;
4059     int found_element = EL_UNDEFINED;
4060     int player_nr = local_player->index_nr;
4061
4062     SCAN_PLAYFIELD(x, y)
4063     {
4064       int element = Feld[x][y];
4065       int content;
4066       int xx, yy;
4067       boolean is_player;
4068
4069       if (level.use_start_element[player_nr] &&
4070           level.start_element[player_nr] == element &&
4071           found_rating < 4)
4072       {
4073         start_x = x;
4074         start_y = y;
4075
4076         found_rating = 4;
4077         found_element = element;
4078       }
4079
4080       if (!IS_CUSTOM_ELEMENT(element))
4081         continue;
4082
4083       if (CAN_CHANGE(element))
4084       {
4085         for (i = 0; i < element_info[element].num_change_pages; i++)
4086         {
4087           /* check for player created from custom element as single target */
4088           content = element_info[element].change_page[i].target_element;
4089           is_player = ELEM_IS_PLAYER(content);
4090
4091           if (is_player && (found_rating < 3 ||
4092                             (found_rating == 3 && element < found_element)))
4093           {
4094             start_x = x;
4095             start_y = y;
4096
4097             found_rating = 3;
4098             found_element = element;
4099           }
4100         }
4101       }
4102
4103       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4104       {
4105         /* check for player created from custom element as explosion content */
4106         content = element_info[element].content.e[xx][yy];
4107         is_player = ELEM_IS_PLAYER(content);
4108
4109         if (is_player && (found_rating < 2 ||
4110                           (found_rating == 2 && element < found_element)))
4111         {
4112           start_x = x + xx - 1;
4113           start_y = y + yy - 1;
4114
4115           found_rating = 2;
4116           found_element = element;
4117         }
4118
4119         if (!CAN_CHANGE(element))
4120           continue;
4121
4122         for (i = 0; i < element_info[element].num_change_pages; i++)
4123         {
4124           /* check for player created from custom element as extended target */
4125           content =
4126             element_info[element].change_page[i].target_content.e[xx][yy];
4127
4128           is_player = ELEM_IS_PLAYER(content);
4129
4130           if (is_player && (found_rating < 1 ||
4131                             (found_rating == 1 && element < found_element)))
4132           {
4133             start_x = x + xx - 1;
4134             start_y = y + yy - 1;
4135
4136             found_rating = 1;
4137             found_element = element;
4138           }
4139         }
4140       }
4141     }
4142
4143     scroll_x = SCROLL_POSITION_X(start_x);
4144     scroll_y = SCROLL_POSITION_Y(start_y);
4145   }
4146   else
4147   {
4148     scroll_x = SCROLL_POSITION_X(local_player->jx);
4149     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4150   }
4151
4152   /* !!! FIX THIS (START) !!! */
4153   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4154   {
4155     InitGameEngine_EM();
4156   }
4157   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4158   {
4159     InitGameEngine_SP();
4160   }
4161   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4162   {
4163     InitGameEngine_MM();
4164   }
4165   else
4166   {
4167     DrawLevel(REDRAW_FIELD);
4168     DrawAllPlayers();
4169
4170     /* after drawing the level, correct some elements */
4171     if (game.timegate_time_left == 0)
4172       CloseAllOpenTimegates();
4173   }
4174
4175   /* blit playfield from scroll buffer to normal back buffer for fading in */
4176   BlitScreenToBitmap(backbuffer);
4177   /* !!! FIX THIS (END) !!! */
4178
4179   DrawMaskedBorder(fade_mask);
4180
4181   FadeIn(fade_mask);
4182
4183 #if 1
4184   // full screen redraw is required at this point in the following cases:
4185   // - special editor door undrawn when game was started from level editor
4186   // - drawing area (playfield) was changed and has to be removed completely
4187   redraw_mask = REDRAW_ALL;
4188   BackToFront();
4189 #endif
4190
4191   if (!game.restart_level)
4192   {
4193     /* copy default game door content to main double buffer */
4194
4195     /* !!! CHECK AGAIN !!! */
4196     SetPanelBackground();
4197     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4198     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4199   }
4200
4201   SetPanelBackground();
4202   SetDrawBackgroundMask(REDRAW_DOOR_1);
4203
4204   UpdateAndDisplayGameControlValues();
4205
4206   if (!game.restart_level)
4207   {
4208     UnmapGameButtons();
4209     UnmapTapeButtons();
4210
4211     FreeGameButtons();
4212     CreateGameButtons();
4213
4214     MapGameButtons();
4215     MapTapeButtons();
4216
4217     /* copy actual game door content to door double buffer for OpenDoor() */
4218     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4219
4220     OpenDoor(DOOR_OPEN_ALL);
4221
4222     KeyboardAutoRepeatOffUnlessAutoplay();
4223
4224 #if DEBUG_INIT_PLAYER
4225     if (options.debug)
4226     {
4227       printf("Player status (final):\n");
4228
4229       for (i = 0; i < MAX_PLAYERS; i++)
4230       {
4231         struct PlayerInfo *player = &stored_player[i];
4232
4233         printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4234                i + 1,
4235                player->present,
4236                player->connected,
4237                player->connected_locally,
4238                player->connected_network,
4239                player->active);
4240
4241         if (local_player == player)
4242           printf(" (local player)");
4243
4244         printf("\n");
4245       }
4246     }
4247 #endif
4248   }
4249
4250   UnmapAllGadgets();
4251
4252   MapGameButtons();
4253   MapTapeButtons();
4254
4255   if (!game.restart_level && !tape.playing)
4256   {
4257     LevelStats_incPlayed(level_nr);
4258
4259     SaveLevelSetup_SeriesInfo();
4260   }
4261
4262   game.restart_level = FALSE;
4263   game.restart_game_message = NULL;
4264
4265   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4266     InitGameActions_MM();
4267
4268   SaveEngineSnapshotToListInitial();
4269
4270   if (!game.restart_level)
4271   {
4272     PlaySound(SND_GAME_STARTING);
4273
4274     if (setup.sound_music)
4275       PlayLevelMusic();
4276   }
4277 }
4278
4279 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4280                         int actual_player_x, int actual_player_y)
4281 {
4282   /* this is used for non-R'n'D game engines to update certain engine values */
4283
4284   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4285   {
4286     actual_player_x = correctLevelPosX_EM(actual_player_x);
4287     actual_player_y = correctLevelPosY_EM(actual_player_y);
4288   }
4289
4290   /* needed to determine if sounds are played within the visible screen area */
4291   scroll_x = actual_scroll_x;
4292   scroll_y = actual_scroll_y;
4293
4294   /* needed to get player position for "follow finger" playing input method */
4295   local_player->jx = actual_player_x;
4296   local_player->jy = actual_player_y;
4297 }
4298
4299 void InitMovDir(int x, int y)
4300 {
4301   int i, element = Feld[x][y];
4302   static int xy[4][2] =
4303   {
4304     {  0, +1 },
4305     { +1,  0 },
4306     {  0, -1 },
4307     { -1,  0 }
4308   };
4309   static int direction[3][4] =
4310   {
4311     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4312     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4313     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4314   };
4315
4316   switch (element)
4317   {
4318     case EL_BUG_RIGHT:
4319     case EL_BUG_UP:
4320     case EL_BUG_LEFT:
4321     case EL_BUG_DOWN:
4322       Feld[x][y] = EL_BUG;
4323       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4324       break;
4325
4326     case EL_SPACESHIP_RIGHT:
4327     case EL_SPACESHIP_UP:
4328     case EL_SPACESHIP_LEFT:
4329     case EL_SPACESHIP_DOWN:
4330       Feld[x][y] = EL_SPACESHIP;
4331       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4332       break;
4333
4334     case EL_BD_BUTTERFLY_RIGHT:
4335     case EL_BD_BUTTERFLY_UP:
4336     case EL_BD_BUTTERFLY_LEFT:
4337     case EL_BD_BUTTERFLY_DOWN:
4338       Feld[x][y] = EL_BD_BUTTERFLY;
4339       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4340       break;
4341
4342     case EL_BD_FIREFLY_RIGHT:
4343     case EL_BD_FIREFLY_UP:
4344     case EL_BD_FIREFLY_LEFT:
4345     case EL_BD_FIREFLY_DOWN:
4346       Feld[x][y] = EL_BD_FIREFLY;
4347       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4348       break;
4349
4350     case EL_PACMAN_RIGHT:
4351     case EL_PACMAN_UP:
4352     case EL_PACMAN_LEFT:
4353     case EL_PACMAN_DOWN:
4354       Feld[x][y] = EL_PACMAN;
4355       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4356       break;
4357
4358     case EL_YAMYAM_LEFT:
4359     case EL_YAMYAM_RIGHT:
4360     case EL_YAMYAM_UP:
4361     case EL_YAMYAM_DOWN:
4362       Feld[x][y] = EL_YAMYAM;
4363       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4364       break;
4365
4366     case EL_SP_SNIKSNAK:
4367       MovDir[x][y] = MV_UP;
4368       break;
4369
4370     case EL_SP_ELECTRON:
4371       MovDir[x][y] = MV_LEFT;
4372       break;
4373
4374     case EL_MOLE_LEFT:
4375     case EL_MOLE_RIGHT:
4376     case EL_MOLE_UP:
4377     case EL_MOLE_DOWN:
4378       Feld[x][y] = EL_MOLE;
4379       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4380       break;
4381
4382     default:
4383       if (IS_CUSTOM_ELEMENT(element))
4384       {
4385         struct ElementInfo *ei = &element_info[element];
4386         int move_direction_initial = ei->move_direction_initial;
4387         int move_pattern = ei->move_pattern;
4388
4389         if (move_direction_initial == MV_START_PREVIOUS)
4390         {
4391           if (MovDir[x][y] != MV_NONE)
4392             return;
4393
4394           move_direction_initial = MV_START_AUTOMATIC;
4395         }
4396
4397         if (move_direction_initial == MV_START_RANDOM)
4398           MovDir[x][y] = 1 << RND(4);
4399         else if (move_direction_initial & MV_ANY_DIRECTION)
4400           MovDir[x][y] = move_direction_initial;
4401         else if (move_pattern == MV_ALL_DIRECTIONS ||
4402                  move_pattern == MV_TURNING_LEFT ||
4403                  move_pattern == MV_TURNING_RIGHT ||
4404                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4405                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4406                  move_pattern == MV_TURNING_RANDOM)
4407           MovDir[x][y] = 1 << RND(4);
4408         else if (move_pattern == MV_HORIZONTAL)
4409           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4410         else if (move_pattern == MV_VERTICAL)
4411           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4412         else if (move_pattern & MV_ANY_DIRECTION)
4413           MovDir[x][y] = element_info[element].move_pattern;
4414         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4415                  move_pattern == MV_ALONG_RIGHT_SIDE)
4416         {
4417           /* use random direction as default start direction */
4418           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4419             MovDir[x][y] = 1 << RND(4);
4420
4421           for (i = 0; i < NUM_DIRECTIONS; i++)
4422           {
4423             int x1 = x + xy[i][0];
4424             int y1 = y + xy[i][1];
4425
4426             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4427             {
4428               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4429                 MovDir[x][y] = direction[0][i];
4430               else
4431                 MovDir[x][y] = direction[1][i];
4432
4433               break;
4434             }
4435           }
4436         }                
4437       }
4438       else
4439       {
4440         MovDir[x][y] = 1 << RND(4);
4441
4442         if (element != EL_BUG &&
4443             element != EL_SPACESHIP &&
4444             element != EL_BD_BUTTERFLY &&
4445             element != EL_BD_FIREFLY)
4446           break;
4447
4448         for (i = 0; i < NUM_DIRECTIONS; i++)
4449         {
4450           int x1 = x + xy[i][0];
4451           int y1 = y + xy[i][1];
4452
4453           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4454           {
4455             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4456             {
4457               MovDir[x][y] = direction[0][i];
4458               break;
4459             }
4460             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4461                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4462             {
4463               MovDir[x][y] = direction[1][i];
4464               break;
4465             }
4466           }
4467         }
4468       }
4469       break;
4470   }
4471
4472   GfxDir[x][y] = MovDir[x][y];
4473 }
4474
4475 void InitAmoebaNr(int x, int y)
4476 {
4477   int i;
4478   int group_nr = AmoebeNachbarNr(x, y);
4479
4480   if (group_nr == 0)
4481   {
4482     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4483     {
4484       if (AmoebaCnt[i] == 0)
4485       {
4486         group_nr = i;
4487         break;
4488       }
4489     }
4490   }
4491
4492   AmoebaNr[x][y] = group_nr;
4493   AmoebaCnt[group_nr]++;
4494   AmoebaCnt2[group_nr]++;
4495 }
4496
4497 static void PlayerWins(struct PlayerInfo *player)
4498 {
4499   player->LevelSolved = TRUE;
4500   player->GameOver = TRUE;
4501
4502   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4503                          level.native_em_level->lev->score :
4504                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4505                          game_mm.score :
4506                          player->score);
4507   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4508                           MM_HEALTH(game_mm.laser_overload_value) :
4509                           player->health);
4510
4511   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4512                                       TimeLeft);
4513   player->LevelSolved_CountingScore = player->score_final;
4514   player->LevelSolved_CountingHealth = player->health_final;
4515 }
4516
4517 void GameWon()
4518 {
4519   static int time_count_steps;
4520   static int time, time_final;
4521   static int score, score_final;
4522   static int health, health_final;
4523   static int game_over_delay_1 = 0;
4524   static int game_over_delay_2 = 0;
4525   static int game_over_delay_3 = 0;
4526   int game_over_delay_value_1 = 50;
4527   int game_over_delay_value_2 = 25;
4528   int game_over_delay_value_3 = 50;
4529
4530   if (!local_player->LevelSolved_GameWon)
4531   {
4532     int i;
4533
4534     /* do not start end game actions before the player stops moving (to exit) */
4535     if (local_player->MovPos)
4536       return;
4537
4538     local_player->LevelSolved_GameWon = TRUE;
4539     local_player->LevelSolved_SaveTape = tape.recording;
4540     local_player->LevelSolved_SaveScore = !tape.playing;
4541
4542     if (!tape.playing)
4543     {
4544       LevelStats_incSolved(level_nr);
4545
4546       SaveLevelSetup_SeriesInfo();
4547     }
4548
4549     if (tape.auto_play)         /* tape might already be stopped here */
4550       tape.auto_play_level_solved = TRUE;
4551
4552     TapeStop();
4553
4554     game_over_delay_1 = 0;
4555     game_over_delay_2 = 0;
4556     game_over_delay_3 = game_over_delay_value_3;
4557
4558     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4559     score = score_final = local_player->score_final;
4560     health = health_final = local_player->health_final;
4561
4562     if (level.score[SC_TIME_BONUS] > 0)
4563     {
4564       if (TimeLeft > 0)
4565       {
4566         time_final = 0;
4567         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4568       }
4569       else if (game.no_time_limit && TimePlayed < 999)
4570       {
4571         time_final = 999;
4572         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4573       }
4574
4575       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4576
4577       game_over_delay_1 = game_over_delay_value_1;
4578
4579       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4580       {
4581         health_final = 0;
4582         score_final += health * level.score[SC_TIME_BONUS];
4583
4584         game_over_delay_2 = game_over_delay_value_2;
4585       }
4586
4587       local_player->score_final = score_final;
4588       local_player->health_final = health_final;
4589     }
4590
4591     if (level_editor_test_game)
4592     {
4593       time = time_final;
4594       score = score_final;
4595
4596       local_player->LevelSolved_CountingTime = time;
4597       local_player->LevelSolved_CountingScore = score;
4598
4599       game_panel_controls[GAME_PANEL_TIME].value = time;
4600       game_panel_controls[GAME_PANEL_SCORE].value = score;
4601
4602       DisplayGameControlValues();
4603     }
4604
4605     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4606     {
4607       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4608       {
4609         /* close exit door after last player */
4610         if ((AllPlayersGone &&
4611              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4612               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4613               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4614             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4615             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4616         {
4617           int element = Feld[ExitX][ExitY];
4618
4619           Feld[ExitX][ExitY] =
4620             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4621              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4622              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4623              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4624              EL_EM_STEEL_EXIT_CLOSING);
4625
4626           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4627         }
4628
4629         /* player disappears */
4630         DrawLevelField(ExitX, ExitY);
4631       }
4632
4633       for (i = 0; i < MAX_PLAYERS; i++)
4634       {
4635         struct PlayerInfo *player = &stored_player[i];
4636
4637         if (player->present)
4638         {
4639           RemovePlayer(player);
4640
4641           /* player disappears */
4642           DrawLevelField(player->jx, player->jy);
4643         }
4644       }
4645     }
4646
4647     PlaySound(SND_GAME_WINNING);
4648   }
4649
4650   if (game_over_delay_1 > 0)
4651   {
4652     game_over_delay_1--;
4653
4654     return;
4655   }
4656
4657   if (time != time_final)
4658   {
4659     int time_to_go = ABS(time_final - time);
4660     int time_count_dir = (time < time_final ? +1 : -1);
4661
4662     if (time_to_go < time_count_steps)
4663       time_count_steps = 1;
4664
4665     time  += time_count_steps * time_count_dir;
4666     score += time_count_steps * level.score[SC_TIME_BONUS];
4667
4668     local_player->LevelSolved_CountingTime = time;
4669     local_player->LevelSolved_CountingScore = score;
4670
4671     game_panel_controls[GAME_PANEL_TIME].value = time;
4672     game_panel_controls[GAME_PANEL_SCORE].value = score;
4673
4674     DisplayGameControlValues();
4675
4676     if (time == time_final)
4677       StopSound(SND_GAME_LEVELTIME_BONUS);
4678     else if (setup.sound_loops)
4679       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4680     else
4681       PlaySound(SND_GAME_LEVELTIME_BONUS);
4682
4683     return;
4684   }
4685
4686   if (game_over_delay_2 > 0)
4687   {
4688     game_over_delay_2--;
4689
4690     return;
4691   }
4692
4693   if (health != health_final)
4694   {
4695     int health_count_dir = (health < health_final ? +1 : -1);
4696
4697     health += health_count_dir;
4698     score  += level.score[SC_TIME_BONUS];
4699
4700     local_player->LevelSolved_CountingHealth = health;
4701     local_player->LevelSolved_CountingScore = score;
4702
4703     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4704     game_panel_controls[GAME_PANEL_SCORE].value = score;
4705
4706     DisplayGameControlValues();
4707
4708     if (health == health_final)
4709       StopSound(SND_GAME_LEVELTIME_BONUS);
4710     else if (setup.sound_loops)
4711       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4712     else
4713       PlaySound(SND_GAME_LEVELTIME_BONUS);
4714
4715     return;
4716   }
4717
4718   local_player->LevelSolved_PanelOff = TRUE;
4719
4720   if (game_over_delay_3 > 0)
4721   {
4722     game_over_delay_3--;
4723
4724     return;
4725   }
4726
4727   GameEnd();
4728 }
4729
4730 void GameEnd()
4731 {
4732   int hi_pos;
4733   boolean raise_level = FALSE;
4734
4735   local_player->LevelSolved_GameEnd = TRUE;
4736
4737   if (local_player->LevelSolved_SaveTape)
4738   {
4739     /* make sure that request dialog to save tape does not open door again */
4740     if (!global.use_envelope_request)
4741       CloseDoor(DOOR_CLOSE_1);
4742
4743     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4744   }
4745
4746   /* if no tape is to be saved, close both doors simultaneously */
4747   CloseDoor(DOOR_CLOSE_ALL);
4748
4749   if (level_editor_test_game)
4750   {
4751     SetGameStatus(GAME_MODE_MAIN);
4752
4753     DrawMainMenu();
4754
4755     return;
4756   }
4757
4758   if (!local_player->LevelSolved_SaveScore)
4759   {
4760     SetGameStatus(GAME_MODE_MAIN);
4761
4762     DrawMainMenu();
4763
4764     return;
4765   }
4766
4767   if (level_nr == leveldir_current->handicap_level)
4768   {
4769     leveldir_current->handicap_level++;
4770
4771     SaveLevelSetup_SeriesInfo();
4772   }
4773
4774   if (setup.increment_levels &&
4775       level_nr < leveldir_current->last_level)
4776     raise_level = TRUE;                 /* advance to next level */
4777
4778   if ((hi_pos = NewHiScore()) >= 0) 
4779   {
4780     SetGameStatus(GAME_MODE_SCORES);
4781
4782     DrawHallOfFame(hi_pos);
4783
4784     if (raise_level)
4785     {
4786       level_nr++;
4787       TapeErase();
4788     }
4789   }
4790   else
4791   {
4792     SetGameStatus(GAME_MODE_MAIN);
4793
4794     if (raise_level)
4795     {
4796       level_nr++;
4797       TapeErase();
4798     }
4799
4800     DrawMainMenu();
4801   }
4802 }
4803
4804 int NewHiScore()
4805 {
4806   int k, l;
4807   int position = -1;
4808   boolean one_score_entry_per_name = !program.many_scores_per_name;
4809
4810   LoadScore(level_nr);
4811
4812   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4813       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4814     return -1;
4815
4816   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4817   {
4818     if (local_player->score_final > highscore[k].Score)
4819     {
4820       /* player has made it to the hall of fame */
4821
4822       if (k < MAX_SCORE_ENTRIES - 1)
4823       {
4824         int m = MAX_SCORE_ENTRIES - 1;
4825
4826         if (one_score_entry_per_name)
4827         {
4828           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4829             if (strEqual(setup.player_name, highscore[l].Name))
4830               m = l;
4831
4832           if (m == k)   /* player's new highscore overwrites his old one */
4833             goto put_into_list;
4834         }
4835
4836         for (l = m; l > k; l--)
4837         {
4838           strcpy(highscore[l].Name, highscore[l - 1].Name);
4839           highscore[l].Score = highscore[l - 1].Score;
4840         }
4841       }
4842
4843       put_into_list:
4844
4845       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4846       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4847       highscore[k].Score = local_player->score_final; 
4848       position = k;
4849
4850       break;
4851     }
4852     else if (one_score_entry_per_name &&
4853              !strncmp(setup.player_name, highscore[k].Name,
4854                       MAX_PLAYER_NAME_LEN))
4855       break;    /* player already there with a higher score */
4856   }
4857
4858   if (position >= 0) 
4859     SaveScore(level_nr);
4860
4861   return position;
4862 }
4863
4864 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4865 {
4866   int element = Feld[x][y];
4867   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4868   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4869   int horiz_move = (dx != 0);
4870   int sign = (horiz_move ? dx : dy);
4871   int step = sign * element_info[element].move_stepsize;
4872
4873   /* special values for move stepsize for spring and things on conveyor belt */
4874   if (horiz_move)
4875   {
4876     if (CAN_FALL(element) &&
4877         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4878       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4879     else if (element == EL_SPRING)
4880       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4881   }
4882
4883   return step;
4884 }
4885
4886 inline static int getElementMoveStepsize(int x, int y)
4887 {
4888   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4889 }
4890
4891 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4892 {
4893   if (player->GfxAction != action || player->GfxDir != dir)
4894   {
4895     player->GfxAction = action;
4896     player->GfxDir = dir;
4897     player->Frame = 0;
4898     player->StepFrame = 0;
4899   }
4900 }
4901
4902 static void ResetGfxFrame(int x, int y)
4903 {
4904   // profiling showed that "autotest" spends 10~20% of its time in this function
4905   if (DrawingDeactivatedField())
4906     return;
4907
4908   int element = Feld[x][y];
4909   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4910
4911   if (graphic_info[graphic].anim_global_sync)
4912     GfxFrame[x][y] = FrameCounter;
4913   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4914     GfxFrame[x][y] = CustomValue[x][y];
4915   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4916     GfxFrame[x][y] = element_info[element].collect_score;
4917   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4918     GfxFrame[x][y] = ChangeDelay[x][y];
4919 }
4920
4921 static void ResetGfxAnimation(int x, int y)
4922 {
4923   GfxAction[x][y] = ACTION_DEFAULT;
4924   GfxDir[x][y] = MovDir[x][y];
4925   GfxFrame[x][y] = 0;
4926
4927   ResetGfxFrame(x, y);
4928 }
4929
4930 static void ResetRandomAnimationValue(int x, int y)
4931 {
4932   GfxRandom[x][y] = INIT_GFX_RANDOM();
4933 }
4934
4935 void InitMovingField(int x, int y, int direction)
4936 {
4937   int element = Feld[x][y];
4938   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4939   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4940   int newx = x + dx;
4941   int newy = y + dy;
4942   boolean is_moving_before, is_moving_after;
4943
4944   /* check if element was/is moving or being moved before/after mode change */
4945   is_moving_before = (WasJustMoving[x][y] != 0);
4946   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4947
4948   /* reset animation only for moving elements which change direction of moving
4949      or which just started or stopped moving
4950      (else CEs with property "can move" / "not moving" are reset each frame) */
4951   if (is_moving_before != is_moving_after ||
4952       direction != MovDir[x][y])
4953     ResetGfxAnimation(x, y);
4954
4955   MovDir[x][y] = direction;
4956   GfxDir[x][y] = direction;
4957
4958   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4959                      direction == MV_DOWN && CAN_FALL(element) ?
4960                      ACTION_FALLING : ACTION_MOVING);
4961
4962   /* this is needed for CEs with property "can move" / "not moving" */
4963
4964   if (is_moving_after)
4965   {
4966     if (Feld[newx][newy] == EL_EMPTY)
4967       Feld[newx][newy] = EL_BLOCKED;
4968
4969     MovDir[newx][newy] = MovDir[x][y];
4970
4971     CustomValue[newx][newy] = CustomValue[x][y];
4972
4973     GfxFrame[newx][newy] = GfxFrame[x][y];
4974     GfxRandom[newx][newy] = GfxRandom[x][y];
4975     GfxAction[newx][newy] = GfxAction[x][y];
4976     GfxDir[newx][newy] = GfxDir[x][y];
4977   }
4978 }
4979
4980 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4981 {
4982   int direction = MovDir[x][y];
4983   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4984   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4985
4986   *goes_to_x = newx;
4987   *goes_to_y = newy;
4988 }
4989
4990 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4991 {
4992   int oldx = x, oldy = y;
4993   int direction = MovDir[x][y];
4994
4995   if (direction == MV_LEFT)
4996     oldx++;
4997   else if (direction == MV_RIGHT)
4998     oldx--;
4999   else if (direction == MV_UP)
5000     oldy++;
5001   else if (direction == MV_DOWN)
5002     oldy--;
5003
5004   *comes_from_x = oldx;
5005   *comes_from_y = oldy;
5006 }
5007
5008 int MovingOrBlocked2Element(int x, int y)
5009 {
5010   int element = Feld[x][y];
5011
5012   if (element == EL_BLOCKED)
5013   {
5014     int oldx, oldy;
5015
5016     Blocked2Moving(x, y, &oldx, &oldy);
5017     return Feld[oldx][oldy];
5018   }
5019   else
5020     return element;
5021 }
5022
5023 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5024 {
5025   /* like MovingOrBlocked2Element(), but if element is moving
5026      and (x,y) is the field the moving element is just leaving,
5027      return EL_BLOCKED instead of the element value */
5028   int element = Feld[x][y];
5029
5030   if (IS_MOVING(x, y))
5031   {
5032     if (element == EL_BLOCKED)
5033     {
5034       int oldx, oldy;
5035
5036       Blocked2Moving(x, y, &oldx, &oldy);
5037       return Feld[oldx][oldy];
5038     }
5039     else
5040       return EL_BLOCKED;
5041   }
5042   else
5043     return element;
5044 }
5045
5046 static void RemoveField(int x, int y)
5047 {
5048   Feld[x][y] = EL_EMPTY;
5049
5050   MovPos[x][y] = 0;
5051   MovDir[x][y] = 0;
5052   MovDelay[x][y] = 0;
5053
5054   CustomValue[x][y] = 0;
5055
5056   AmoebaNr[x][y] = 0;
5057   ChangeDelay[x][y] = 0;
5058   ChangePage[x][y] = -1;
5059   Pushed[x][y] = FALSE;
5060
5061   GfxElement[x][y] = EL_UNDEFINED;
5062   GfxAction[x][y] = ACTION_DEFAULT;
5063   GfxDir[x][y] = MV_NONE;
5064 }
5065
5066 void RemoveMovingField(int x, int y)
5067 {
5068   int oldx = x, oldy = y, newx = x, newy = y;
5069   int element = Feld[x][y];
5070   int next_element = EL_UNDEFINED;
5071
5072   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5073     return;
5074
5075   if (IS_MOVING(x, y))
5076   {
5077     Moving2Blocked(x, y, &newx, &newy);
5078
5079     if (Feld[newx][newy] != EL_BLOCKED)
5080     {
5081       /* element is moving, but target field is not free (blocked), but
5082          already occupied by something different (example: acid pool);
5083          in this case, only remove the moving field, but not the target */
5084
5085       RemoveField(oldx, oldy);
5086
5087       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5088
5089       TEST_DrawLevelField(oldx, oldy);
5090
5091       return;
5092     }
5093   }
5094   else if (element == EL_BLOCKED)
5095   {
5096     Blocked2Moving(x, y, &oldx, &oldy);
5097     if (!IS_MOVING(oldx, oldy))
5098       return;
5099   }
5100
5101   if (element == EL_BLOCKED &&
5102       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5103        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5104        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5105        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5106        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5107        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5108     next_element = get_next_element(Feld[oldx][oldy]);
5109
5110   RemoveField(oldx, oldy);
5111   RemoveField(newx, newy);
5112
5113   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5114
5115   if (next_element != EL_UNDEFINED)
5116     Feld[oldx][oldy] = next_element;
5117
5118   TEST_DrawLevelField(oldx, oldy);
5119   TEST_DrawLevelField(newx, newy);
5120 }
5121
5122 void DrawDynamite(int x, int y)
5123 {
5124   int sx = SCREENX(x), sy = SCREENY(y);
5125   int graphic = el2img(Feld[x][y]);
5126   int frame;
5127
5128   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5129     return;
5130
5131   if (IS_WALKABLE_INSIDE(Back[x][y]))
5132     return;
5133
5134   if (Back[x][y])
5135     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5136   else if (Store[x][y])
5137     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5138
5139   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5140
5141   if (Back[x][y] || Store[x][y])
5142     DrawGraphicThruMask(sx, sy, graphic, frame);
5143   else
5144     DrawGraphic(sx, sy, graphic, frame);
5145 }
5146
5147 void CheckDynamite(int x, int y)
5148 {
5149   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5150   {
5151     MovDelay[x][y]--;
5152
5153     if (MovDelay[x][y] != 0)
5154     {
5155       DrawDynamite(x, y);
5156       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5157
5158       return;
5159     }
5160   }
5161
5162   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5163
5164   Bang(x, y);
5165 }
5166
5167 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5168 {
5169   boolean num_checked_players = 0;
5170   int i;
5171
5172   for (i = 0; i < MAX_PLAYERS; i++)
5173   {
5174     if (stored_player[i].active)
5175     {
5176       int sx = stored_player[i].jx;
5177       int sy = stored_player[i].jy;
5178
5179       if (num_checked_players == 0)
5180       {
5181         *sx1 = *sx2 = sx;
5182         *sy1 = *sy2 = sy;
5183       }
5184       else
5185       {
5186         *sx1 = MIN(*sx1, sx);
5187         *sy1 = MIN(*sy1, sy);
5188         *sx2 = MAX(*sx2, sx);
5189         *sy2 = MAX(*sy2, sy);
5190       }
5191
5192       num_checked_players++;
5193     }
5194   }
5195 }
5196
5197 static boolean checkIfAllPlayersFitToScreen_RND()
5198 {
5199   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5200
5201   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5202
5203   return (sx2 - sx1 < SCR_FIELDX &&
5204           sy2 - sy1 < SCR_FIELDY);
5205 }
5206
5207 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5208 {
5209   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5210
5211   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5212
5213   *sx = (sx1 + sx2) / 2;
5214   *sy = (sy1 + sy2) / 2;
5215 }
5216
5217 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5218                         boolean center_screen, boolean quick_relocation)
5219 {
5220   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5221   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5222   boolean no_delay = (tape.warp_forward);
5223   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5224   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5225   int new_scroll_x, new_scroll_y;
5226
5227   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5228   {
5229     /* case 1: quick relocation inside visible screen (without scrolling) */
5230
5231     RedrawPlayfield();
5232
5233     return;
5234   }
5235
5236   if (!level.shifted_relocation || center_screen)
5237   {
5238     /* relocation _with_ centering of screen */
5239
5240     new_scroll_x = SCROLL_POSITION_X(x);
5241     new_scroll_y = SCROLL_POSITION_Y(y);
5242   }
5243   else
5244   {
5245     /* relocation _without_ centering of screen */
5246
5247     int center_scroll_x = SCROLL_POSITION_X(old_x);
5248     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5249     int offset_x = x + (scroll_x - center_scroll_x);
5250     int offset_y = y + (scroll_y - center_scroll_y);
5251
5252     /* for new screen position, apply previous offset to center position */
5253     new_scroll_x = SCROLL_POSITION_X(offset_x);
5254     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5255   }
5256
5257   if (quick_relocation)
5258   {
5259     /* case 2: quick relocation (redraw without visible scrolling) */
5260
5261     scroll_x = new_scroll_x;
5262     scroll_y = new_scroll_y;
5263
5264     RedrawPlayfield();
5265
5266     return;
5267   }
5268
5269   /* case 3: visible relocation (with scrolling to new position) */
5270
5271   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5272
5273   SetVideoFrameDelay(wait_delay_value);
5274
5275   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5276   {
5277     int dx = 0, dy = 0;
5278     int fx = FX, fy = FY;
5279
5280     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5281     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5282
5283     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5284       break;
5285
5286     scroll_x -= dx;
5287     scroll_y -= dy;
5288
5289     fx += dx * TILEX / 2;
5290     fy += dy * TILEY / 2;
5291
5292     ScrollLevel(dx, dy);
5293     DrawAllPlayers();
5294
5295     /* scroll in two steps of half tile size to make things smoother */
5296     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5297
5298     /* scroll second step to align at full tile size */
5299     BlitScreenToBitmap(window);
5300   }
5301
5302   DrawAllPlayers();
5303   BackToFront();
5304
5305   SetVideoFrameDelay(frame_delay_value_old);
5306 }
5307
5308 void RelocatePlayer(int jx, int jy, int el_player_raw)
5309 {
5310   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5311   int player_nr = GET_PLAYER_NR(el_player);
5312   struct PlayerInfo *player = &stored_player[player_nr];
5313   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5314   boolean no_delay = (tape.warp_forward);
5315   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5316   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5317   int old_jx = player->jx;
5318   int old_jy = player->jy;
5319   int old_element = Feld[old_jx][old_jy];
5320   int element = Feld[jx][jy];
5321   boolean player_relocated = (old_jx != jx || old_jy != jy);
5322
5323   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5324   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5325   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5326   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5327   int leave_side_horiz = move_dir_horiz;
5328   int leave_side_vert  = move_dir_vert;
5329   int enter_side = enter_side_horiz | enter_side_vert;
5330   int leave_side = leave_side_horiz | leave_side_vert;
5331
5332   if (player->GameOver)         /* do not reanimate dead player */
5333     return;
5334
5335   if (!player_relocated)        /* no need to relocate the player */
5336     return;
5337
5338   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5339   {
5340     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5341     DrawLevelField(jx, jy);
5342   }
5343
5344   if (player->present)
5345   {
5346     while (player->MovPos)
5347     {
5348       ScrollPlayer(player, SCROLL_GO_ON);
5349       ScrollScreen(NULL, SCROLL_GO_ON);
5350
5351       AdvanceFrameAndPlayerCounters(player->index_nr);
5352
5353       DrawPlayer(player);
5354
5355       BackToFront_WithFrameDelay(wait_delay_value);
5356     }
5357
5358     DrawPlayer(player);         /* needed here only to cleanup last field */
5359     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5360
5361     player->is_moving = FALSE;
5362   }
5363
5364   if (IS_CUSTOM_ELEMENT(old_element))
5365     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5366                                CE_LEFT_BY_PLAYER,
5367                                player->index_bit, leave_side);
5368
5369   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5370                                       CE_PLAYER_LEAVES_X,
5371                                       player->index_bit, leave_side);
5372
5373   Feld[jx][jy] = el_player;
5374   InitPlayerField(jx, jy, el_player, TRUE);
5375
5376   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5377      possible that the relocation target field did not contain a player element,
5378      but a walkable element, to which the new player was relocated -- in this
5379      case, restore that (already initialized!) element on the player field */
5380   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5381   {
5382     Feld[jx][jy] = element;     /* restore previously existing element */
5383   }
5384
5385   /* only visually relocate centered player */
5386   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5387                      FALSE, level.instant_relocation);
5388
5389   TestIfPlayerTouchesBadThing(jx, jy);
5390   TestIfPlayerTouchesCustomElement(jx, jy);
5391
5392   if (IS_CUSTOM_ELEMENT(element))
5393     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5394                                player->index_bit, enter_side);
5395
5396   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5397                                       player->index_bit, enter_side);
5398
5399   if (player->is_switching)
5400   {
5401     /* ensure that relocation while still switching an element does not cause
5402        a new element to be treated as also switched directly after relocation
5403        (this is important for teleporter switches that teleport the player to
5404        a place where another teleporter switch is in the same direction, which
5405        would then incorrectly be treated as immediately switched before the
5406        direction key that caused the switch was released) */
5407
5408     player->switch_x += jx - old_jx;
5409     player->switch_y += jy - old_jy;
5410   }
5411 }
5412
5413 void Explode(int ex, int ey, int phase, int mode)
5414 {
5415   int x, y;
5416   int last_phase;
5417   int border_element;
5418
5419   /* !!! eliminate this variable !!! */
5420   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5421
5422   if (game.explosions_delayed)
5423   {
5424     ExplodeField[ex][ey] = mode;
5425     return;
5426   }
5427
5428   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5429   {
5430     int center_element = Feld[ex][ey];
5431     int artwork_element, explosion_element;     /* set these values later */
5432
5433     /* remove things displayed in background while burning dynamite */
5434     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5435       Back[ex][ey] = 0;
5436
5437     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5438     {
5439       /* put moving element to center field (and let it explode there) */
5440       center_element = MovingOrBlocked2Element(ex, ey);
5441       RemoveMovingField(ex, ey);
5442       Feld[ex][ey] = center_element;
5443     }
5444
5445     /* now "center_element" is finally determined -- set related values now */
5446     artwork_element = center_element;           /* for custom player artwork */
5447     explosion_element = center_element;         /* for custom player artwork */
5448
5449     if (IS_PLAYER(ex, ey))
5450     {
5451       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5452
5453       artwork_element = stored_player[player_nr].artwork_element;
5454
5455       if (level.use_explosion_element[player_nr])
5456       {
5457         explosion_element = level.explosion_element[player_nr];
5458         artwork_element = explosion_element;
5459       }
5460     }
5461
5462     if (mode == EX_TYPE_NORMAL ||
5463         mode == EX_TYPE_CENTER ||
5464         mode == EX_TYPE_CROSS)
5465       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5466
5467     last_phase = element_info[explosion_element].explosion_delay + 1;
5468
5469     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5470     {
5471       int xx = x - ex + 1;
5472       int yy = y - ey + 1;
5473       int element;
5474
5475       if (!IN_LEV_FIELD(x, y) ||
5476           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5477           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5478         continue;
5479
5480       element = Feld[x][y];
5481
5482       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5483       {
5484         element = MovingOrBlocked2Element(x, y);
5485
5486         if (!IS_EXPLOSION_PROOF(element))
5487           RemoveMovingField(x, y);
5488       }
5489
5490       /* indestructible elements can only explode in center (but not flames) */
5491       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5492                                            mode == EX_TYPE_BORDER)) ||
5493           element == EL_FLAMES)
5494         continue;
5495
5496       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5497          behaviour, for example when touching a yamyam that explodes to rocks
5498          with active deadly shield, a rock is created under the player !!! */
5499       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5500 #if 0
5501       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5502           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5503            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5504 #else
5505       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5506 #endif
5507       {
5508         if (IS_ACTIVE_BOMB(element))
5509         {
5510           /* re-activate things under the bomb like gate or penguin */
5511           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5512           Back[x][y] = 0;
5513         }
5514
5515         continue;
5516       }
5517
5518       /* save walkable background elements while explosion on same tile */
5519       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5520           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5521         Back[x][y] = element;
5522
5523       /* ignite explodable elements reached by other explosion */
5524       if (element == EL_EXPLOSION)
5525         element = Store2[x][y];
5526
5527       if (AmoebaNr[x][y] &&
5528           (element == EL_AMOEBA_FULL ||
5529            element == EL_BD_AMOEBA ||
5530            element == EL_AMOEBA_GROWING))
5531       {
5532         AmoebaCnt[AmoebaNr[x][y]]--;
5533         AmoebaCnt2[AmoebaNr[x][y]]--;
5534       }
5535
5536       RemoveField(x, y);
5537
5538       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5539       {
5540         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5541
5542         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5543
5544         if (PLAYERINFO(ex, ey)->use_murphy)
5545           Store[x][y] = EL_EMPTY;
5546       }
5547
5548       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5549          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5550       else if (ELEM_IS_PLAYER(center_element))
5551         Store[x][y] = EL_EMPTY;
5552       else if (center_element == EL_YAMYAM)
5553         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5554       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5555         Store[x][y] = element_info[center_element].content.e[xx][yy];
5556 #if 1
5557       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5558          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5559          otherwise) -- FIX THIS !!! */
5560       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5561         Store[x][y] = element_info[element].content.e[1][1];
5562 #else
5563       else if (!CAN_EXPLODE(element))
5564         Store[x][y] = element_info[element].content.e[1][1];
5565 #endif
5566       else
5567         Store[x][y] = EL_EMPTY;
5568
5569       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5570           center_element == EL_AMOEBA_TO_DIAMOND)
5571         Store2[x][y] = element;
5572
5573       Feld[x][y] = EL_EXPLOSION;
5574       GfxElement[x][y] = artwork_element;
5575
5576       ExplodePhase[x][y] = 1;
5577       ExplodeDelay[x][y] = last_phase;
5578
5579       Stop[x][y] = TRUE;
5580     }
5581
5582     if (center_element == EL_YAMYAM)
5583       game.yamyam_content_nr =
5584         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5585
5586     return;
5587   }
5588
5589   if (Stop[ex][ey])
5590     return;
5591
5592   x = ex;
5593   y = ey;
5594
5595   if (phase == 1)
5596     GfxFrame[x][y] = 0;         /* restart explosion animation */
5597
5598   last_phase = ExplodeDelay[x][y];
5599
5600   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5601
5602   /* this can happen if the player leaves an explosion just in time */
5603   if (GfxElement[x][y] == EL_UNDEFINED)
5604     GfxElement[x][y] = EL_EMPTY;
5605
5606   border_element = Store2[x][y];
5607   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5608     border_element = StorePlayer[x][y];
5609
5610   if (phase == element_info[border_element].ignition_delay ||
5611       phase == last_phase)
5612   {
5613     boolean border_explosion = FALSE;
5614
5615     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5616         !PLAYER_EXPLOSION_PROTECTED(x, y))
5617     {
5618       KillPlayerUnlessExplosionProtected(x, y);
5619       border_explosion = TRUE;
5620     }
5621     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5622     {
5623       Feld[x][y] = Store2[x][y];
5624       Store2[x][y] = 0;
5625       Bang(x, y);
5626       border_explosion = TRUE;
5627     }
5628     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5629     {
5630       AmoebeUmwandeln(x, y);
5631       Store2[x][y] = 0;
5632       border_explosion = TRUE;
5633     }
5634
5635     /* if an element just explodes due to another explosion (chain-reaction),
5636        do not immediately end the new explosion when it was the last frame of
5637        the explosion (as it would be done in the following "if"-statement!) */
5638     if (border_explosion && phase == last_phase)
5639       return;
5640   }
5641
5642   if (phase == last_phase)
5643   {
5644     int element;
5645
5646     element = Feld[x][y] = Store[x][y];
5647     Store[x][y] = Store2[x][y] = 0;
5648     GfxElement[x][y] = EL_UNDEFINED;
5649
5650     /* player can escape from explosions and might therefore be still alive */
5651     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5652         element <= EL_PLAYER_IS_EXPLODING_4)
5653     {
5654       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5655       int explosion_element = EL_PLAYER_1 + player_nr;
5656       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5657       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5658
5659       if (level.use_explosion_element[player_nr])
5660         explosion_element = level.explosion_element[player_nr];
5661
5662       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5663                     element_info[explosion_element].content.e[xx][yy]);
5664     }
5665
5666     /* restore probably existing indestructible background element */
5667     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5668       element = Feld[x][y] = Back[x][y];
5669     Back[x][y] = 0;
5670
5671     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5672     GfxDir[x][y] = MV_NONE;
5673     ChangeDelay[x][y] = 0;
5674     ChangePage[x][y] = -1;
5675
5676     CustomValue[x][y] = 0;
5677
5678     InitField_WithBug2(x, y, FALSE);
5679
5680     TEST_DrawLevelField(x, y);
5681
5682     TestIfElementTouchesCustomElement(x, y);
5683
5684     if (GFX_CRUMBLED(element))
5685       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5686
5687     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5688       StorePlayer[x][y] = 0;
5689
5690     if (ELEM_IS_PLAYER(element))
5691       RelocatePlayer(x, y, element);
5692   }
5693   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5694   {
5695     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5696     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5697
5698     if (phase == delay)
5699       TEST_DrawLevelFieldCrumbled(x, y);
5700
5701     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5702     {
5703       DrawLevelElement(x, y, Back[x][y]);
5704       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5705     }
5706     else if (IS_WALKABLE_UNDER(Back[x][y]))
5707     {
5708       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5709       DrawLevelElementThruMask(x, y, Back[x][y]);
5710     }
5711     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5712       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5713   }
5714 }
5715
5716 void DynaExplode(int ex, int ey)
5717 {
5718   int i, j;
5719   int dynabomb_element = Feld[ex][ey];
5720   int dynabomb_size = 1;
5721   boolean dynabomb_xl = FALSE;
5722   struct PlayerInfo *player;
5723   static int xy[4][2] =
5724   {
5725     { 0, -1 },
5726     { -1, 0 },
5727     { +1, 0 },
5728     { 0, +1 }
5729   };
5730
5731   if (IS_ACTIVE_BOMB(dynabomb_element))
5732   {
5733     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5734     dynabomb_size = player->dynabomb_size;
5735     dynabomb_xl = player->dynabomb_xl;
5736     player->dynabombs_left++;
5737   }
5738
5739   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5740
5741   for (i = 0; i < NUM_DIRECTIONS; i++)
5742   {
5743     for (j = 1; j <= dynabomb_size; j++)
5744     {
5745       int x = ex + j * xy[i][0];
5746       int y = ey + j * xy[i][1];
5747       int element;
5748
5749       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5750         break;
5751
5752       element = Feld[x][y];
5753
5754       /* do not restart explosions of fields with active bombs */
5755       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5756         continue;
5757
5758       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5759
5760       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5761           !IS_DIGGABLE(element) && !dynabomb_xl)
5762         break;
5763     }
5764   }
5765 }
5766
5767 void Bang(int x, int y)
5768 {
5769   int element = MovingOrBlocked2Element(x, y);
5770   int explosion_type = EX_TYPE_NORMAL;
5771
5772   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5773   {
5774     struct PlayerInfo *player = PLAYERINFO(x, y);
5775
5776     element = Feld[x][y] = player->initial_element;
5777
5778     if (level.use_explosion_element[player->index_nr])
5779     {
5780       int explosion_element = level.explosion_element[player->index_nr];
5781
5782       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5783         explosion_type = EX_TYPE_CROSS;
5784       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5785         explosion_type = EX_TYPE_CENTER;
5786     }
5787   }
5788
5789   switch (element)
5790   {
5791     case EL_BUG:
5792     case EL_SPACESHIP:
5793     case EL_BD_BUTTERFLY:
5794     case EL_BD_FIREFLY:
5795     case EL_YAMYAM:
5796     case EL_DARK_YAMYAM:
5797     case EL_ROBOT:
5798     case EL_PACMAN:
5799     case EL_MOLE:
5800       RaiseScoreElement(element);
5801       break;
5802
5803     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5804     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5805     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5806     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5807     case EL_DYNABOMB_INCREASE_NUMBER:
5808     case EL_DYNABOMB_INCREASE_SIZE:
5809     case EL_DYNABOMB_INCREASE_POWER:
5810       explosion_type = EX_TYPE_DYNA;
5811       break;
5812
5813     case EL_DC_LANDMINE:
5814       explosion_type = EX_TYPE_CENTER;
5815       break;
5816
5817     case EL_PENGUIN:
5818     case EL_LAMP:
5819     case EL_LAMP_ACTIVE:
5820     case EL_AMOEBA_TO_DIAMOND:
5821       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5822         explosion_type = EX_TYPE_CENTER;
5823       break;
5824
5825     default:
5826       if (element_info[element].explosion_type == EXPLODES_CROSS)
5827         explosion_type = EX_TYPE_CROSS;
5828       else if (element_info[element].explosion_type == EXPLODES_1X1)
5829         explosion_type = EX_TYPE_CENTER;
5830       break;
5831   }
5832
5833   if (explosion_type == EX_TYPE_DYNA)
5834     DynaExplode(x, y);
5835   else
5836     Explode(x, y, EX_PHASE_START, explosion_type);
5837
5838   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5839 }
5840
5841 void SplashAcid(int x, int y)
5842 {
5843   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5844       (!IN_LEV_FIELD(x - 1, y - 2) ||
5845        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5846     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5847
5848   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5849       (!IN_LEV_FIELD(x + 1, y - 2) ||
5850        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5851     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5852
5853   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5854 }
5855
5856 static void InitBeltMovement()
5857 {
5858   static int belt_base_element[4] =
5859   {
5860     EL_CONVEYOR_BELT_1_LEFT,
5861     EL_CONVEYOR_BELT_2_LEFT,
5862     EL_CONVEYOR_BELT_3_LEFT,
5863     EL_CONVEYOR_BELT_4_LEFT
5864   };
5865   static int belt_base_active_element[4] =
5866   {
5867     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5868     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5869     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5870     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5871   };
5872
5873   int x, y, i, j;
5874
5875   /* set frame order for belt animation graphic according to belt direction */
5876   for (i = 0; i < NUM_BELTS; i++)
5877   {
5878     int belt_nr = i;
5879
5880     for (j = 0; j < NUM_BELT_PARTS; j++)
5881     {
5882       int element = belt_base_active_element[belt_nr] + j;
5883       int graphic_1 = el2img(element);
5884       int graphic_2 = el2panelimg(element);
5885
5886       if (game.belt_dir[i] == MV_LEFT)
5887       {
5888         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5889         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5890       }
5891       else
5892       {
5893         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5894         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5895       }
5896     }
5897   }
5898
5899   SCAN_PLAYFIELD(x, y)
5900   {
5901     int element = Feld[x][y];
5902
5903     for (i = 0; i < NUM_BELTS; i++)
5904     {
5905       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5906       {
5907         int e_belt_nr = getBeltNrFromBeltElement(element);
5908         int belt_nr = i;
5909
5910         if (e_belt_nr == belt_nr)
5911         {
5912           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5913
5914           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5915         }
5916       }
5917     }
5918   }
5919 }
5920
5921 static void ToggleBeltSwitch(int x, int y)
5922 {
5923   static int belt_base_element[4] =
5924   {
5925     EL_CONVEYOR_BELT_1_LEFT,
5926     EL_CONVEYOR_BELT_2_LEFT,
5927     EL_CONVEYOR_BELT_3_LEFT,
5928     EL_CONVEYOR_BELT_4_LEFT
5929   };
5930   static int belt_base_active_element[4] =
5931   {
5932     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5933     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5934     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5935     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5936   };
5937   static int belt_base_switch_element[4] =
5938   {
5939     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5940     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5941     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5942     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5943   };
5944   static int belt_move_dir[4] =
5945   {
5946     MV_LEFT,
5947     MV_NONE,
5948     MV_RIGHT,
5949     MV_NONE,
5950   };
5951
5952   int element = Feld[x][y];
5953   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5954   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5955   int belt_dir = belt_move_dir[belt_dir_nr];
5956   int xx, yy, i;
5957
5958   if (!IS_BELT_SWITCH(element))
5959     return;
5960
5961   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5962   game.belt_dir[belt_nr] = belt_dir;
5963
5964   if (belt_dir_nr == 3)
5965     belt_dir_nr = 1;
5966
5967   /* set frame order for belt animation graphic according to belt direction */
5968   for (i = 0; i < NUM_BELT_PARTS; i++)
5969   {
5970     int element = belt_base_active_element[belt_nr] + i;
5971     int graphic_1 = el2img(element);
5972     int graphic_2 = el2panelimg(element);
5973
5974     if (belt_dir == MV_LEFT)
5975     {
5976       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5977       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5978     }
5979     else
5980     {
5981       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5982       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5983     }
5984   }
5985
5986   SCAN_PLAYFIELD(xx, yy)
5987   {
5988     int element = Feld[xx][yy];
5989
5990     if (IS_BELT_SWITCH(element))
5991     {
5992       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5993
5994       if (e_belt_nr == belt_nr)
5995       {
5996         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5997         TEST_DrawLevelField(xx, yy);
5998       }
5999     }
6000     else if (IS_BELT(element) && belt_dir != MV_NONE)
6001     {
6002       int e_belt_nr = getBeltNrFromBeltElement(element);
6003
6004       if (e_belt_nr == belt_nr)
6005       {
6006         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6007
6008         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6009         TEST_DrawLevelField(xx, yy);
6010       }
6011     }
6012     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6013     {
6014       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6015
6016       if (e_belt_nr == belt_nr)
6017       {
6018         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6019
6020         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6021         TEST_DrawLevelField(xx, yy);
6022       }
6023     }
6024   }
6025 }
6026
6027 static void ToggleSwitchgateSwitch(int x, int y)
6028 {
6029   int xx, yy;
6030
6031   game.switchgate_pos = !game.switchgate_pos;
6032
6033   SCAN_PLAYFIELD(xx, yy)
6034   {
6035     int element = Feld[xx][yy];
6036
6037     if (element == EL_SWITCHGATE_SWITCH_UP)
6038     {
6039       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6040       TEST_DrawLevelField(xx, yy);
6041     }
6042     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6043     {
6044       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6045       TEST_DrawLevelField(xx, yy);
6046     }
6047     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6048     {
6049       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6050       TEST_DrawLevelField(xx, yy);
6051     }
6052     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6053     {
6054       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6055       TEST_DrawLevelField(xx, yy);
6056     }
6057     else if (element == EL_SWITCHGATE_OPEN ||
6058              element == EL_SWITCHGATE_OPENING)
6059     {
6060       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6061
6062       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6063     }
6064     else if (element == EL_SWITCHGATE_CLOSED ||
6065              element == EL_SWITCHGATE_CLOSING)
6066     {
6067       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6068
6069       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6070     }
6071   }
6072 }
6073
6074 static int getInvisibleActiveFromInvisibleElement(int element)
6075 {
6076   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6077           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6078           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6079           element);
6080 }
6081
6082 static int getInvisibleFromInvisibleActiveElement(int element)
6083 {
6084   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6085           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6086           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6087           element);
6088 }
6089
6090 static void RedrawAllLightSwitchesAndInvisibleElements()
6091 {
6092   int x, y;
6093
6094   SCAN_PLAYFIELD(x, y)
6095   {
6096     int element = Feld[x][y];
6097
6098     if (element == EL_LIGHT_SWITCH &&
6099         game.light_time_left > 0)
6100     {
6101       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6102       TEST_DrawLevelField(x, y);
6103     }
6104     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6105              game.light_time_left == 0)
6106     {
6107       Feld[x][y] = EL_LIGHT_SWITCH;
6108       TEST_DrawLevelField(x, y);
6109     }
6110     else if (element == EL_EMC_DRIPPER &&
6111              game.light_time_left > 0)
6112     {
6113       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6114       TEST_DrawLevelField(x, y);
6115     }
6116     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6117              game.light_time_left == 0)
6118     {
6119       Feld[x][y] = EL_EMC_DRIPPER;
6120       TEST_DrawLevelField(x, y);
6121     }
6122     else if (element == EL_INVISIBLE_STEELWALL ||
6123              element == EL_INVISIBLE_WALL ||
6124              element == EL_INVISIBLE_SAND)
6125     {
6126       if (game.light_time_left > 0)
6127         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6128
6129       TEST_DrawLevelField(x, y);
6130
6131       /* uncrumble neighbour fields, if needed */
6132       if (element == EL_INVISIBLE_SAND)
6133         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6134     }
6135     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6136              element == EL_INVISIBLE_WALL_ACTIVE ||
6137              element == EL_INVISIBLE_SAND_ACTIVE)
6138     {
6139       if (game.light_time_left == 0)
6140         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6141
6142       TEST_DrawLevelField(x, y);
6143
6144       /* re-crumble neighbour fields, if needed */
6145       if (element == EL_INVISIBLE_SAND)
6146         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6147     }
6148   }
6149 }
6150
6151 static void RedrawAllInvisibleElementsForLenses()
6152 {
6153   int x, y;
6154
6155   SCAN_PLAYFIELD(x, y)
6156   {
6157     int element = Feld[x][y];
6158
6159     if (element == EL_EMC_DRIPPER &&
6160         game.lenses_time_left > 0)
6161     {
6162       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6163       TEST_DrawLevelField(x, y);
6164     }
6165     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6166              game.lenses_time_left == 0)
6167     {
6168       Feld[x][y] = EL_EMC_DRIPPER;
6169       TEST_DrawLevelField(x, y);
6170     }
6171     else if (element == EL_INVISIBLE_STEELWALL ||
6172              element == EL_INVISIBLE_WALL ||
6173              element == EL_INVISIBLE_SAND)
6174     {
6175       if (game.lenses_time_left > 0)
6176         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6177
6178       TEST_DrawLevelField(x, y);
6179
6180       /* uncrumble neighbour fields, if needed */
6181       if (element == EL_INVISIBLE_SAND)
6182         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6183     }
6184     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6185              element == EL_INVISIBLE_WALL_ACTIVE ||
6186              element == EL_INVISIBLE_SAND_ACTIVE)
6187     {
6188       if (game.lenses_time_left == 0)
6189         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6190
6191       TEST_DrawLevelField(x, y);
6192
6193       /* re-crumble neighbour fields, if needed */
6194       if (element == EL_INVISIBLE_SAND)
6195         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6196     }
6197   }
6198 }
6199
6200 static void RedrawAllInvisibleElementsForMagnifier()
6201 {
6202   int x, y;
6203
6204   SCAN_PLAYFIELD(x, y)
6205   {
6206     int element = Feld[x][y];
6207
6208     if (element == EL_EMC_FAKE_GRASS &&
6209         game.magnify_time_left > 0)
6210     {
6211       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6212       TEST_DrawLevelField(x, y);
6213     }
6214     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6215              game.magnify_time_left == 0)
6216     {
6217       Feld[x][y] = EL_EMC_FAKE_GRASS;
6218       TEST_DrawLevelField(x, y);
6219     }
6220     else if (IS_GATE_GRAY(element) &&
6221              game.magnify_time_left > 0)
6222     {
6223       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6224                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6225                     IS_EM_GATE_GRAY(element) ?
6226                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6227                     IS_EMC_GATE_GRAY(element) ?
6228                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6229                     IS_DC_GATE_GRAY(element) ?
6230                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6231                     element);
6232       TEST_DrawLevelField(x, y);
6233     }
6234     else if (IS_GATE_GRAY_ACTIVE(element) &&
6235              game.magnify_time_left == 0)
6236     {
6237       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6238                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6239                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6240                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6241                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6242                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6243                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6244                     EL_DC_GATE_WHITE_GRAY :
6245                     element);
6246       TEST_DrawLevelField(x, y);
6247     }
6248   }
6249 }
6250
6251 static void ToggleLightSwitch(int x, int y)
6252 {
6253   int element = Feld[x][y];
6254
6255   game.light_time_left =
6256     (element == EL_LIGHT_SWITCH ?
6257      level.time_light * FRAMES_PER_SECOND : 0);
6258
6259   RedrawAllLightSwitchesAndInvisibleElements();
6260 }
6261
6262 static void ActivateTimegateSwitch(int x, int y)
6263 {
6264   int xx, yy;
6265
6266   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6267
6268   SCAN_PLAYFIELD(xx, yy)
6269   {
6270     int element = Feld[xx][yy];
6271
6272     if (element == EL_TIMEGATE_CLOSED ||
6273         element == EL_TIMEGATE_CLOSING)
6274     {
6275       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6276       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6277     }
6278
6279     /*
6280     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6281     {
6282       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6283       TEST_DrawLevelField(xx, yy);
6284     }
6285     */
6286
6287   }
6288
6289   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6290                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6291 }
6292
6293 void Impact(int x, int y)
6294 {
6295   boolean last_line = (y == lev_fieldy - 1);
6296   boolean object_hit = FALSE;
6297   boolean impact = (last_line || object_hit);
6298   int element = Feld[x][y];
6299   int smashed = EL_STEELWALL;
6300
6301   if (!last_line)       /* check if element below was hit */
6302   {
6303     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6304       return;
6305
6306     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6307                                          MovDir[x][y + 1] != MV_DOWN ||
6308                                          MovPos[x][y + 1] <= TILEY / 2));
6309
6310     /* do not smash moving elements that left the smashed field in time */
6311     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6312         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6313       object_hit = FALSE;
6314
6315 #if USE_QUICKSAND_IMPACT_BUGFIX
6316     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6317     {
6318       RemoveMovingField(x, y + 1);
6319       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6320       Feld[x][y + 2] = EL_ROCK;
6321       TEST_DrawLevelField(x, y + 2);
6322
6323       object_hit = TRUE;
6324     }
6325
6326     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6327     {
6328       RemoveMovingField(x, y + 1);
6329       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6330       Feld[x][y + 2] = EL_ROCK;
6331       TEST_DrawLevelField(x, y + 2);
6332
6333       object_hit = TRUE;
6334     }
6335 #endif
6336
6337     if (object_hit)
6338       smashed = MovingOrBlocked2Element(x, y + 1);
6339
6340     impact = (last_line || object_hit);
6341   }
6342
6343   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6344   {
6345     SplashAcid(x, y + 1);
6346     return;
6347   }
6348
6349   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6350   /* only reset graphic animation if graphic really changes after impact */
6351   if (impact &&
6352       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6353   {
6354     ResetGfxAnimation(x, y);
6355     TEST_DrawLevelField(x, y);
6356   }
6357
6358   if (impact && CAN_EXPLODE_IMPACT(element))
6359   {
6360     Bang(x, y);
6361     return;
6362   }
6363   else if (impact && element == EL_PEARL &&
6364            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6365   {
6366     ResetGfxAnimation(x, y);
6367
6368     Feld[x][y] = EL_PEARL_BREAKING;
6369     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6370     return;
6371   }
6372   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6373   {
6374     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6375
6376     return;
6377   }
6378
6379   if (impact && element == EL_AMOEBA_DROP)
6380   {
6381     if (object_hit && IS_PLAYER(x, y + 1))
6382       KillPlayerUnlessEnemyProtected(x, y + 1);
6383     else if (object_hit && smashed == EL_PENGUIN)
6384       Bang(x, y + 1);
6385     else
6386     {
6387       Feld[x][y] = EL_AMOEBA_GROWING;
6388       Store[x][y] = EL_AMOEBA_WET;
6389
6390       ResetRandomAnimationValue(x, y);
6391     }
6392     return;
6393   }
6394
6395   if (object_hit)               /* check which object was hit */
6396   {
6397     if ((CAN_PASS_MAGIC_WALL(element) && 
6398          (smashed == EL_MAGIC_WALL ||
6399           smashed == EL_BD_MAGIC_WALL)) ||
6400         (CAN_PASS_DC_MAGIC_WALL(element) &&
6401          smashed == EL_DC_MAGIC_WALL))
6402     {
6403       int xx, yy;
6404       int activated_magic_wall =
6405         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6406          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6407          EL_DC_MAGIC_WALL_ACTIVE);
6408
6409       /* activate magic wall / mill */
6410       SCAN_PLAYFIELD(xx, yy)
6411       {
6412         if (Feld[xx][yy] == smashed)
6413           Feld[xx][yy] = activated_magic_wall;
6414       }
6415
6416       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6417       game.magic_wall_active = TRUE;
6418
6419       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6420                             SND_MAGIC_WALL_ACTIVATING :
6421                             smashed == EL_BD_MAGIC_WALL ?
6422                             SND_BD_MAGIC_WALL_ACTIVATING :
6423                             SND_DC_MAGIC_WALL_ACTIVATING));
6424     }
6425
6426     if (IS_PLAYER(x, y + 1))
6427     {
6428       if (CAN_SMASH_PLAYER(element))
6429       {
6430         KillPlayerUnlessEnemyProtected(x, y + 1);
6431         return;
6432       }
6433     }
6434     else if (smashed == EL_PENGUIN)
6435     {
6436       if (CAN_SMASH_PLAYER(element))
6437       {
6438         Bang(x, y + 1);
6439         return;
6440       }
6441     }
6442     else if (element == EL_BD_DIAMOND)
6443     {
6444       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6445       {
6446         Bang(x, y + 1);
6447         return;
6448       }
6449     }
6450     else if (((element == EL_SP_INFOTRON ||
6451                element == EL_SP_ZONK) &&
6452               (smashed == EL_SP_SNIKSNAK ||
6453                smashed == EL_SP_ELECTRON ||
6454                smashed == EL_SP_DISK_ORANGE)) ||
6455              (element == EL_SP_INFOTRON &&
6456               smashed == EL_SP_DISK_YELLOW))
6457     {
6458       Bang(x, y + 1);
6459       return;
6460     }
6461     else if (CAN_SMASH_EVERYTHING(element))
6462     {
6463       if (IS_CLASSIC_ENEMY(smashed) ||
6464           CAN_EXPLODE_SMASHED(smashed))
6465       {
6466         Bang(x, y + 1);
6467         return;
6468       }
6469       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6470       {
6471         if (smashed == EL_LAMP ||
6472             smashed == EL_LAMP_ACTIVE)
6473         {
6474           Bang(x, y + 1);
6475           return;
6476         }
6477         else if (smashed == EL_NUT)
6478         {
6479           Feld[x][y + 1] = EL_NUT_BREAKING;
6480           PlayLevelSound(x, y, SND_NUT_BREAKING);
6481           RaiseScoreElement(EL_NUT);
6482           return;
6483         }
6484         else if (smashed == EL_PEARL)
6485         {
6486           ResetGfxAnimation(x, y);
6487
6488           Feld[x][y + 1] = EL_PEARL_BREAKING;
6489           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6490           return;
6491         }
6492         else if (smashed == EL_DIAMOND)
6493         {
6494           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6495           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6496           return;
6497         }
6498         else if (IS_BELT_SWITCH(smashed))
6499         {
6500           ToggleBeltSwitch(x, y + 1);
6501         }
6502         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6503                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6504                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6505                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6506         {
6507           ToggleSwitchgateSwitch(x, y + 1);
6508         }
6509         else if (smashed == EL_LIGHT_SWITCH ||
6510                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6511         {
6512           ToggleLightSwitch(x, y + 1);
6513         }
6514         else
6515         {
6516           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6517
6518           CheckElementChangeBySide(x, y + 1, smashed, element,
6519                                    CE_SWITCHED, CH_SIDE_TOP);
6520           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6521                                             CH_SIDE_TOP);
6522         }
6523       }
6524       else
6525       {
6526         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6527       }
6528     }
6529   }
6530
6531   /* play sound of magic wall / mill */
6532   if (!last_line &&
6533       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6534        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6535        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6536   {
6537     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6538       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6539     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6540       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6541     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6542       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6543
6544     return;
6545   }
6546
6547   /* play sound of object that hits the ground */
6548   if (last_line || object_hit)
6549     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6550 }
6551
6552 inline static void TurnRoundExt(int x, int y)
6553 {
6554   static struct
6555   {
6556     int dx, dy;
6557   } move_xy[] =
6558   {
6559     {  0,  0 },
6560     { -1,  0 },
6561     { +1,  0 },
6562     {  0,  0 },
6563     {  0, -1 },
6564     {  0,  0 }, { 0, 0 }, { 0, 0 },
6565     {  0, +1 }
6566   };
6567   static struct
6568   {
6569     int left, right, back;
6570   } turn[] =
6571   {
6572     { 0,        0,              0        },
6573     { MV_DOWN,  MV_UP,          MV_RIGHT },
6574     { MV_UP,    MV_DOWN,        MV_LEFT  },
6575     { 0,        0,              0        },
6576     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6577     { 0,        0,              0        },
6578     { 0,        0,              0        },
6579     { 0,        0,              0        },
6580     { MV_RIGHT, MV_LEFT,        MV_UP    }
6581   };
6582
6583   int element = Feld[x][y];
6584   int move_pattern = element_info[element].move_pattern;
6585
6586   int old_move_dir = MovDir[x][y];
6587   int left_dir  = turn[old_move_dir].left;
6588   int right_dir = turn[old_move_dir].right;
6589   int back_dir  = turn[old_move_dir].back;
6590
6591   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6592   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6593   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6594   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6595
6596   int left_x  = x + left_dx,  left_y  = y + left_dy;
6597   int right_x = x + right_dx, right_y = y + right_dy;
6598   int move_x  = x + move_dx,  move_y  = y + move_dy;
6599
6600   int xx, yy;
6601
6602   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6603   {
6604     TestIfBadThingTouchesOtherBadThing(x, y);
6605
6606     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6607       MovDir[x][y] = right_dir;
6608     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6609       MovDir[x][y] = left_dir;
6610
6611     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6612       MovDelay[x][y] = 9;
6613     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6614       MovDelay[x][y] = 1;
6615   }
6616   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6617   {
6618     TestIfBadThingTouchesOtherBadThing(x, y);
6619
6620     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6621       MovDir[x][y] = left_dir;
6622     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6623       MovDir[x][y] = right_dir;
6624
6625     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6626       MovDelay[x][y] = 9;
6627     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6628       MovDelay[x][y] = 1;
6629   }
6630   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6631   {
6632     TestIfBadThingTouchesOtherBadThing(x, y);
6633
6634     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6635       MovDir[x][y] = left_dir;
6636     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6637       MovDir[x][y] = right_dir;
6638
6639     if (MovDir[x][y] != old_move_dir)
6640       MovDelay[x][y] = 9;
6641   }
6642   else if (element == EL_YAMYAM)
6643   {
6644     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6645     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6646
6647     if (can_turn_left && can_turn_right)
6648       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6649     else if (can_turn_left)
6650       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6651     else if (can_turn_right)
6652       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6653     else
6654       MovDir[x][y] = back_dir;
6655
6656     MovDelay[x][y] = 16 + 16 * RND(3);
6657   }
6658   else if (element == EL_DARK_YAMYAM)
6659   {
6660     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6661                                                          left_x, left_y);
6662     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6663                                                          right_x, right_y);
6664
6665     if (can_turn_left && can_turn_right)
6666       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6667     else if (can_turn_left)
6668       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6669     else if (can_turn_right)
6670       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6671     else
6672       MovDir[x][y] = back_dir;
6673
6674     MovDelay[x][y] = 16 + 16 * RND(3);
6675   }
6676   else if (element == EL_PACMAN)
6677   {
6678     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6679     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6680
6681     if (can_turn_left && can_turn_right)
6682       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6683     else if (can_turn_left)
6684       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6685     else if (can_turn_right)
6686       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6687     else
6688       MovDir[x][y] = back_dir;
6689
6690     MovDelay[x][y] = 6 + RND(40);
6691   }
6692   else if (element == EL_PIG)
6693   {
6694     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6695     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6696     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6697     boolean should_turn_left, should_turn_right, should_move_on;
6698     int rnd_value = 24;
6699     int rnd = RND(rnd_value);
6700
6701     should_turn_left = (can_turn_left &&
6702                         (!can_move_on ||
6703                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6704                                                    y + back_dy + left_dy)));
6705     should_turn_right = (can_turn_right &&
6706                          (!can_move_on ||
6707                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6708                                                     y + back_dy + right_dy)));
6709     should_move_on = (can_move_on &&
6710                       (!can_turn_left ||
6711                        !can_turn_right ||
6712                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6713                                                  y + move_dy + left_dy) ||
6714                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6715                                                  y + move_dy + right_dy)));
6716
6717     if (should_turn_left || should_turn_right || should_move_on)
6718     {
6719       if (should_turn_left && should_turn_right && should_move_on)
6720         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6721                         rnd < 2 * rnd_value / 3 ? right_dir :
6722                         old_move_dir);
6723       else if (should_turn_left && should_turn_right)
6724         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6725       else if (should_turn_left && should_move_on)
6726         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6727       else if (should_turn_right && should_move_on)
6728         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6729       else if (should_turn_left)
6730         MovDir[x][y] = left_dir;
6731       else if (should_turn_right)
6732         MovDir[x][y] = right_dir;
6733       else if (should_move_on)
6734         MovDir[x][y] = old_move_dir;
6735     }
6736     else if (can_move_on && rnd > rnd_value / 8)
6737       MovDir[x][y] = old_move_dir;
6738     else if (can_turn_left && can_turn_right)
6739       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6740     else if (can_turn_left && rnd > rnd_value / 8)
6741       MovDir[x][y] = left_dir;
6742     else if (can_turn_right && rnd > rnd_value/8)
6743       MovDir[x][y] = right_dir;
6744     else
6745       MovDir[x][y] = back_dir;
6746
6747     xx = x + move_xy[MovDir[x][y]].dx;
6748     yy = y + move_xy[MovDir[x][y]].dy;
6749
6750     if (!IN_LEV_FIELD(xx, yy) ||
6751         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6752       MovDir[x][y] = old_move_dir;
6753
6754     MovDelay[x][y] = 0;
6755   }
6756   else if (element == EL_DRAGON)
6757   {
6758     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6759     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6760     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6761     int rnd_value = 24;
6762     int rnd = RND(rnd_value);
6763
6764     if (can_move_on && rnd > rnd_value / 8)
6765       MovDir[x][y] = old_move_dir;
6766     else if (can_turn_left && can_turn_right)
6767       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6768     else if (can_turn_left && rnd > rnd_value / 8)
6769       MovDir[x][y] = left_dir;
6770     else if (can_turn_right && rnd > rnd_value / 8)
6771       MovDir[x][y] = right_dir;
6772     else
6773       MovDir[x][y] = back_dir;
6774
6775     xx = x + move_xy[MovDir[x][y]].dx;
6776     yy = y + move_xy[MovDir[x][y]].dy;
6777
6778     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6779       MovDir[x][y] = old_move_dir;
6780
6781     MovDelay[x][y] = 0;
6782   }
6783   else if (element == EL_MOLE)
6784   {
6785     boolean can_move_on =
6786       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6787                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6788                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6789     if (!can_move_on)
6790     {
6791       boolean can_turn_left =
6792         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6793                               IS_AMOEBOID(Feld[left_x][left_y])));
6794
6795       boolean can_turn_right =
6796         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6797                               IS_AMOEBOID(Feld[right_x][right_y])));
6798
6799       if (can_turn_left && can_turn_right)
6800         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6801       else if (can_turn_left)
6802         MovDir[x][y] = left_dir;
6803       else
6804         MovDir[x][y] = right_dir;
6805     }
6806
6807     if (MovDir[x][y] != old_move_dir)
6808       MovDelay[x][y] = 9;
6809   }
6810   else if (element == EL_BALLOON)
6811   {
6812     MovDir[x][y] = game.wind_direction;
6813     MovDelay[x][y] = 0;
6814   }
6815   else if (element == EL_SPRING)
6816   {
6817     if (MovDir[x][y] & MV_HORIZONTAL)
6818     {
6819       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6820           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6821       {
6822         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6823         ResetGfxAnimation(move_x, move_y);
6824         TEST_DrawLevelField(move_x, move_y);
6825
6826         MovDir[x][y] = back_dir;
6827       }
6828       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6829                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6830         MovDir[x][y] = MV_NONE;
6831     }
6832
6833     MovDelay[x][y] = 0;
6834   }
6835   else if (element == EL_ROBOT ||
6836            element == EL_SATELLITE ||
6837            element == EL_PENGUIN ||
6838            element == EL_EMC_ANDROID)
6839   {
6840     int attr_x = -1, attr_y = -1;
6841
6842     if (AllPlayersGone)
6843     {
6844       attr_x = ExitX;
6845       attr_y = ExitY;
6846     }
6847     else
6848     {
6849       int i;
6850
6851       for (i = 0; i < MAX_PLAYERS; i++)
6852       {
6853         struct PlayerInfo *player = &stored_player[i];
6854         int jx = player->jx, jy = player->jy;
6855
6856         if (!player->active)
6857           continue;
6858
6859         if (attr_x == -1 ||
6860             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6861         {
6862           attr_x = jx;
6863           attr_y = jy;
6864         }
6865       }
6866     }
6867
6868     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6869         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6870          game.engine_version < VERSION_IDENT(3,1,0,0)))
6871     {
6872       attr_x = ZX;
6873       attr_y = ZY;
6874     }
6875
6876     if (element == EL_PENGUIN)
6877     {
6878       int i;
6879       static int xy[4][2] =
6880       {
6881         { 0, -1 },
6882         { -1, 0 },
6883         { +1, 0 },
6884         { 0, +1 }
6885       };
6886
6887       for (i = 0; i < NUM_DIRECTIONS; i++)
6888       {
6889         int ex = x + xy[i][0];
6890         int ey = y + xy[i][1];
6891
6892         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6893                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6894                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6895                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6896         {
6897           attr_x = ex;
6898           attr_y = ey;
6899           break;
6900         }
6901       }
6902     }
6903
6904     MovDir[x][y] = MV_NONE;
6905     if (attr_x < x)
6906       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6907     else if (attr_x > x)
6908       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6909     if (attr_y < y)
6910       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6911     else if (attr_y > y)
6912       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6913
6914     if (element == EL_ROBOT)
6915     {
6916       int newx, newy;
6917
6918       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6919         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6920       Moving2Blocked(x, y, &newx, &newy);
6921
6922       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6923         MovDelay[x][y] = 8 + 8 * !RND(3);
6924       else
6925         MovDelay[x][y] = 16;
6926     }
6927     else if (element == EL_PENGUIN)
6928     {
6929       int newx, newy;
6930
6931       MovDelay[x][y] = 1;
6932
6933       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6934       {
6935         boolean first_horiz = RND(2);
6936         int new_move_dir = MovDir[x][y];
6937
6938         MovDir[x][y] =
6939           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6940         Moving2Blocked(x, y, &newx, &newy);
6941
6942         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6943           return;
6944
6945         MovDir[x][y] =
6946           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6947         Moving2Blocked(x, y, &newx, &newy);
6948
6949         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6950           return;
6951
6952         MovDir[x][y] = old_move_dir;
6953         return;
6954       }
6955     }
6956     else if (element == EL_SATELLITE)
6957     {
6958       int newx, newy;
6959
6960       MovDelay[x][y] = 1;
6961
6962       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6963       {
6964         boolean first_horiz = RND(2);
6965         int new_move_dir = MovDir[x][y];
6966
6967         MovDir[x][y] =
6968           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6969         Moving2Blocked(x, y, &newx, &newy);
6970
6971         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6972           return;
6973
6974         MovDir[x][y] =
6975           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6976         Moving2Blocked(x, y, &newx, &newy);
6977
6978         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6979           return;
6980
6981         MovDir[x][y] = old_move_dir;
6982         return;
6983       }
6984     }
6985     else if (element == EL_EMC_ANDROID)
6986     {
6987       static int check_pos[16] =
6988       {
6989         -1,             /*  0 => (invalid)          */
6990         7,              /*  1 => MV_LEFT            */
6991         3,              /*  2 => MV_RIGHT           */
6992         -1,             /*  3 => (invalid)          */
6993         1,              /*  4 =>            MV_UP   */
6994         0,              /*  5 => MV_LEFT  | MV_UP   */
6995         2,              /*  6 => MV_RIGHT | MV_UP   */
6996         -1,             /*  7 => (invalid)          */
6997         5,              /*  8 =>            MV_DOWN */
6998         6,              /*  9 => MV_LEFT  | MV_DOWN */
6999         4,              /* 10 => MV_RIGHT | MV_DOWN */
7000         -1,             /* 11 => (invalid)          */
7001         -1,             /* 12 => (invalid)          */
7002         -1,             /* 13 => (invalid)          */
7003         -1,             /* 14 => (invalid)          */
7004         -1,             /* 15 => (invalid)          */
7005       };
7006       static struct
7007       {
7008         int dx, dy;
7009         int dir;
7010       } check_xy[8] =
7011       {
7012         { -1, -1,       MV_LEFT  | MV_UP   },
7013         {  0, -1,                  MV_UP   },
7014         { +1, -1,       MV_RIGHT | MV_UP   },
7015         { +1,  0,       MV_RIGHT           },
7016         { +1, +1,       MV_RIGHT | MV_DOWN },
7017         {  0, +1,                  MV_DOWN },
7018         { -1, +1,       MV_LEFT  | MV_DOWN },
7019         { -1,  0,       MV_LEFT            },
7020       };
7021       int start_pos, check_order;
7022       boolean can_clone = FALSE;
7023       int i;
7024
7025       /* check if there is any free field around current position */
7026       for (i = 0; i < 8; i++)
7027       {
7028         int newx = x + check_xy[i].dx;
7029         int newy = y + check_xy[i].dy;
7030
7031         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7032         {
7033           can_clone = TRUE;
7034
7035           break;
7036         }
7037       }
7038
7039       if (can_clone)            /* randomly find an element to clone */
7040       {
7041         can_clone = FALSE;
7042
7043         start_pos = check_pos[RND(8)];
7044         check_order = (RND(2) ? -1 : +1);
7045
7046         for (i = 0; i < 8; i++)
7047         {
7048           int pos_raw = start_pos + i * check_order;
7049           int pos = (pos_raw + 8) % 8;
7050           int newx = x + check_xy[pos].dx;
7051           int newy = y + check_xy[pos].dy;
7052
7053           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7054           {
7055             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7056             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7057
7058             Store[x][y] = Feld[newx][newy];
7059
7060             can_clone = TRUE;
7061
7062             break;
7063           }
7064         }
7065       }
7066
7067       if (can_clone)            /* randomly find a direction to move */
7068       {
7069         can_clone = FALSE;
7070
7071         start_pos = check_pos[RND(8)];
7072         check_order = (RND(2) ? -1 : +1);
7073
7074         for (i = 0; i < 8; i++)
7075         {
7076           int pos_raw = start_pos + i * check_order;
7077           int pos = (pos_raw + 8) % 8;
7078           int newx = x + check_xy[pos].dx;
7079           int newy = y + check_xy[pos].dy;
7080           int new_move_dir = check_xy[pos].dir;
7081
7082           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7083           {
7084             MovDir[x][y] = new_move_dir;
7085             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7086
7087             can_clone = TRUE;
7088
7089             break;
7090           }
7091         }
7092       }
7093
7094       if (can_clone)            /* cloning and moving successful */
7095         return;
7096
7097       /* cannot clone -- try to move towards player */
7098
7099       start_pos = check_pos[MovDir[x][y] & 0x0f];
7100       check_order = (RND(2) ? -1 : +1);
7101
7102       for (i = 0; i < 3; i++)
7103       {
7104         /* first check start_pos, then previous/next or (next/previous) pos */
7105         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7106         int pos = (pos_raw + 8) % 8;
7107         int newx = x + check_xy[pos].dx;
7108         int newy = y + check_xy[pos].dy;
7109         int new_move_dir = check_xy[pos].dir;
7110
7111         if (IS_PLAYER(newx, newy))
7112           break;
7113
7114         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7115         {
7116           MovDir[x][y] = new_move_dir;
7117           MovDelay[x][y] = level.android_move_time * 8 + 1;
7118
7119           break;
7120         }
7121       }
7122     }
7123   }
7124   else if (move_pattern == MV_TURNING_LEFT ||
7125            move_pattern == MV_TURNING_RIGHT ||
7126            move_pattern == MV_TURNING_LEFT_RIGHT ||
7127            move_pattern == MV_TURNING_RIGHT_LEFT ||
7128            move_pattern == MV_TURNING_RANDOM ||
7129            move_pattern == MV_ALL_DIRECTIONS)
7130   {
7131     boolean can_turn_left =
7132       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7133     boolean can_turn_right =
7134       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7135
7136     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7137       return;
7138
7139     if (move_pattern == MV_TURNING_LEFT)
7140       MovDir[x][y] = left_dir;
7141     else if (move_pattern == MV_TURNING_RIGHT)
7142       MovDir[x][y] = right_dir;
7143     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7144       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7145     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7146       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7147     else if (move_pattern == MV_TURNING_RANDOM)
7148       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7149                       can_turn_right && !can_turn_left ? right_dir :
7150                       RND(2) ? left_dir : right_dir);
7151     else if (can_turn_left && can_turn_right)
7152       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7153     else if (can_turn_left)
7154       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7155     else if (can_turn_right)
7156       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7157     else
7158       MovDir[x][y] = back_dir;
7159
7160     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7161   }
7162   else if (move_pattern == MV_HORIZONTAL ||
7163            move_pattern == MV_VERTICAL)
7164   {
7165     if (move_pattern & old_move_dir)
7166       MovDir[x][y] = back_dir;
7167     else if (move_pattern == MV_HORIZONTAL)
7168       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7169     else if (move_pattern == MV_VERTICAL)
7170       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7171
7172     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7173   }
7174   else if (move_pattern & MV_ANY_DIRECTION)
7175   {
7176     MovDir[x][y] = move_pattern;
7177     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7178   }
7179   else if (move_pattern & MV_WIND_DIRECTION)
7180   {
7181     MovDir[x][y] = game.wind_direction;
7182     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7183   }
7184   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7185   {
7186     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7187       MovDir[x][y] = left_dir;
7188     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7189       MovDir[x][y] = right_dir;
7190
7191     if (MovDir[x][y] != old_move_dir)
7192       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7193   }
7194   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7195   {
7196     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7197       MovDir[x][y] = right_dir;
7198     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7199       MovDir[x][y] = left_dir;
7200
7201     if (MovDir[x][y] != old_move_dir)
7202       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7203   }
7204   else if (move_pattern == MV_TOWARDS_PLAYER ||
7205            move_pattern == MV_AWAY_FROM_PLAYER)
7206   {
7207     int attr_x = -1, attr_y = -1;
7208     int newx, newy;
7209     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7210
7211     if (AllPlayersGone)
7212     {
7213       attr_x = ExitX;
7214       attr_y = ExitY;
7215     }
7216     else
7217     {
7218       int i;
7219
7220       for (i = 0; i < MAX_PLAYERS; i++)
7221       {
7222         struct PlayerInfo *player = &stored_player[i];
7223         int jx = player->jx, jy = player->jy;
7224
7225         if (!player->active)
7226           continue;
7227
7228         if (attr_x == -1 ||
7229             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7230         {
7231           attr_x = jx;
7232           attr_y = jy;
7233         }
7234       }
7235     }
7236
7237     MovDir[x][y] = MV_NONE;
7238     if (attr_x < x)
7239       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7240     else if (attr_x > x)
7241       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7242     if (attr_y < y)
7243       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7244     else if (attr_y > y)
7245       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7246
7247     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7248
7249     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7250     {
7251       boolean first_horiz = RND(2);
7252       int new_move_dir = MovDir[x][y];
7253
7254       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7255       {
7256         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7257         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7258
7259         return;
7260       }
7261
7262       MovDir[x][y] =
7263         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7264       Moving2Blocked(x, y, &newx, &newy);
7265
7266       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7267         return;
7268
7269       MovDir[x][y] =
7270         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7271       Moving2Blocked(x, y, &newx, &newy);
7272
7273       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7274         return;
7275
7276       MovDir[x][y] = old_move_dir;
7277     }
7278   }
7279   else if (move_pattern == MV_WHEN_PUSHED ||
7280            move_pattern == MV_WHEN_DROPPED)
7281   {
7282     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7283       MovDir[x][y] = MV_NONE;
7284
7285     MovDelay[x][y] = 0;
7286   }
7287   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7288   {
7289     static int test_xy[7][2] =
7290     {
7291       { 0, -1 },
7292       { -1, 0 },
7293       { +1, 0 },
7294       { 0, +1 },
7295       { 0, -1 },
7296       { -1, 0 },
7297       { +1, 0 },
7298     };
7299     static int test_dir[7] =
7300     {
7301       MV_UP,
7302       MV_LEFT,
7303       MV_RIGHT,
7304       MV_DOWN,
7305       MV_UP,
7306       MV_LEFT,
7307       MV_RIGHT,
7308     };
7309     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7310     int move_preference = -1000000;     /* start with very low preference */
7311     int new_move_dir = MV_NONE;
7312     int start_test = RND(4);
7313     int i;
7314
7315     for (i = 0; i < NUM_DIRECTIONS; i++)
7316     {
7317       int move_dir = test_dir[start_test + i];
7318       int move_dir_preference;
7319
7320       xx = x + test_xy[start_test + i][0];
7321       yy = y + test_xy[start_test + i][1];
7322
7323       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7324           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7325       {
7326         new_move_dir = move_dir;
7327
7328         break;
7329       }
7330
7331       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7332         continue;
7333
7334       move_dir_preference = -1 * RunnerVisit[xx][yy];
7335       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7336         move_dir_preference = PlayerVisit[xx][yy];
7337
7338       if (move_dir_preference > move_preference)
7339       {
7340         /* prefer field that has not been visited for the longest time */
7341         move_preference = move_dir_preference;
7342         new_move_dir = move_dir;
7343       }
7344       else if (move_dir_preference == move_preference &&
7345                move_dir == old_move_dir)
7346       {
7347         /* prefer last direction when all directions are preferred equally */
7348         move_preference = move_dir_preference;
7349         new_move_dir = move_dir;
7350       }
7351     }
7352
7353     MovDir[x][y] = new_move_dir;
7354     if (old_move_dir != new_move_dir)
7355       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7356   }
7357 }
7358
7359 static void TurnRound(int x, int y)
7360 {
7361   int direction = MovDir[x][y];
7362
7363   TurnRoundExt(x, y);
7364
7365   GfxDir[x][y] = MovDir[x][y];
7366
7367   if (direction != MovDir[x][y])
7368     GfxFrame[x][y] = 0;
7369
7370   if (MovDelay[x][y])
7371     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7372
7373   ResetGfxFrame(x, y);
7374 }
7375
7376 static boolean JustBeingPushed(int x, int y)
7377 {
7378   int i;
7379
7380   for (i = 0; i < MAX_PLAYERS; i++)
7381   {
7382     struct PlayerInfo *player = &stored_player[i];
7383
7384     if (player->active && player->is_pushing && player->MovPos)
7385     {
7386       int next_jx = player->jx + (player->jx - player->last_jx);
7387       int next_jy = player->jy + (player->jy - player->last_jy);
7388
7389       if (x == next_jx && y == next_jy)
7390         return TRUE;
7391     }
7392   }
7393
7394   return FALSE;
7395 }
7396
7397 void StartMoving(int x, int y)
7398 {
7399   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7400   int element = Feld[x][y];
7401
7402   if (Stop[x][y])
7403     return;
7404
7405   if (MovDelay[x][y] == 0)
7406     GfxAction[x][y] = ACTION_DEFAULT;
7407
7408   if (CAN_FALL(element) && y < lev_fieldy - 1)
7409   {
7410     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7411         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7412       if (JustBeingPushed(x, y))
7413         return;
7414
7415     if (element == EL_QUICKSAND_FULL)
7416     {
7417       if (IS_FREE(x, y + 1))
7418       {
7419         InitMovingField(x, y, MV_DOWN);
7420         started_moving = TRUE;
7421
7422         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7423 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7424         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7425           Store[x][y] = EL_ROCK;
7426 #else
7427         Store[x][y] = EL_ROCK;
7428 #endif
7429
7430         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7431       }
7432       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7433       {
7434         if (!MovDelay[x][y])
7435         {
7436           MovDelay[x][y] = TILEY + 1;
7437
7438           ResetGfxAnimation(x, y);
7439           ResetGfxAnimation(x, y + 1);
7440         }
7441
7442         if (MovDelay[x][y])
7443         {
7444           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7445           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7446
7447           MovDelay[x][y]--;
7448           if (MovDelay[x][y])
7449             return;
7450         }
7451
7452         Feld[x][y] = EL_QUICKSAND_EMPTY;
7453         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7454         Store[x][y + 1] = Store[x][y];
7455         Store[x][y] = 0;
7456
7457         PlayLevelSoundAction(x, y, ACTION_FILLING);
7458       }
7459       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7460       {
7461         if (!MovDelay[x][y])
7462         {
7463           MovDelay[x][y] = TILEY + 1;
7464
7465           ResetGfxAnimation(x, y);
7466           ResetGfxAnimation(x, y + 1);
7467         }
7468
7469         if (MovDelay[x][y])
7470         {
7471           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7472           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7473
7474           MovDelay[x][y]--;
7475           if (MovDelay[x][y])
7476             return;
7477         }
7478
7479         Feld[x][y] = EL_QUICKSAND_EMPTY;
7480         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7481         Store[x][y + 1] = Store[x][y];
7482         Store[x][y] = 0;
7483
7484         PlayLevelSoundAction(x, y, ACTION_FILLING);
7485       }
7486     }
7487     else if (element == EL_QUICKSAND_FAST_FULL)
7488     {
7489       if (IS_FREE(x, y + 1))
7490       {
7491         InitMovingField(x, y, MV_DOWN);
7492         started_moving = TRUE;
7493
7494         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7495 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7496         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7497           Store[x][y] = EL_ROCK;
7498 #else
7499         Store[x][y] = EL_ROCK;
7500 #endif
7501
7502         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7503       }
7504       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7505       {
7506         if (!MovDelay[x][y])
7507         {
7508           MovDelay[x][y] = TILEY + 1;
7509
7510           ResetGfxAnimation(x, y);
7511           ResetGfxAnimation(x, y + 1);
7512         }
7513
7514         if (MovDelay[x][y])
7515         {
7516           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7517           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7518
7519           MovDelay[x][y]--;
7520           if (MovDelay[x][y])
7521             return;
7522         }
7523
7524         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7525         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7526         Store[x][y + 1] = Store[x][y];
7527         Store[x][y] = 0;
7528
7529         PlayLevelSoundAction(x, y, ACTION_FILLING);
7530       }
7531       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7532       {
7533         if (!MovDelay[x][y])
7534         {
7535           MovDelay[x][y] = TILEY + 1;
7536
7537           ResetGfxAnimation(x, y);
7538           ResetGfxAnimation(x, y + 1);
7539         }
7540
7541         if (MovDelay[x][y])
7542         {
7543           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7544           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7545
7546           MovDelay[x][y]--;
7547           if (MovDelay[x][y])
7548             return;
7549         }
7550
7551         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7552         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7553         Store[x][y + 1] = Store[x][y];
7554         Store[x][y] = 0;
7555
7556         PlayLevelSoundAction(x, y, ACTION_FILLING);
7557       }
7558     }
7559     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7560              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7561     {
7562       InitMovingField(x, y, MV_DOWN);
7563       started_moving = TRUE;
7564
7565       Feld[x][y] = EL_QUICKSAND_FILLING;
7566       Store[x][y] = element;
7567
7568       PlayLevelSoundAction(x, y, ACTION_FILLING);
7569     }
7570     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7571              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7572     {
7573       InitMovingField(x, y, MV_DOWN);
7574       started_moving = TRUE;
7575
7576       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7577       Store[x][y] = element;
7578
7579       PlayLevelSoundAction(x, y, ACTION_FILLING);
7580     }
7581     else if (element == EL_MAGIC_WALL_FULL)
7582     {
7583       if (IS_FREE(x, y + 1))
7584       {
7585         InitMovingField(x, y, MV_DOWN);
7586         started_moving = TRUE;
7587
7588         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7589         Store[x][y] = EL_CHANGED(Store[x][y]);
7590       }
7591       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7592       {
7593         if (!MovDelay[x][y])
7594           MovDelay[x][y] = TILEY / 4 + 1;
7595
7596         if (MovDelay[x][y])
7597         {
7598           MovDelay[x][y]--;
7599           if (MovDelay[x][y])
7600             return;
7601         }
7602
7603         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7604         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7605         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7606         Store[x][y] = 0;
7607       }
7608     }
7609     else if (element == EL_BD_MAGIC_WALL_FULL)
7610     {
7611       if (IS_FREE(x, y + 1))
7612       {
7613         InitMovingField(x, y, MV_DOWN);
7614         started_moving = TRUE;
7615
7616         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7617         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7618       }
7619       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7620       {
7621         if (!MovDelay[x][y])
7622           MovDelay[x][y] = TILEY / 4 + 1;
7623
7624         if (MovDelay[x][y])
7625         {
7626           MovDelay[x][y]--;
7627           if (MovDelay[x][y])
7628             return;
7629         }
7630
7631         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7632         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7633         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7634         Store[x][y] = 0;
7635       }
7636     }
7637     else if (element == EL_DC_MAGIC_WALL_FULL)
7638     {
7639       if (IS_FREE(x, y + 1))
7640       {
7641         InitMovingField(x, y, MV_DOWN);
7642         started_moving = TRUE;
7643
7644         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7645         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7646       }
7647       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7648       {
7649         if (!MovDelay[x][y])
7650           MovDelay[x][y] = TILEY / 4 + 1;
7651
7652         if (MovDelay[x][y])
7653         {
7654           MovDelay[x][y]--;
7655           if (MovDelay[x][y])
7656             return;
7657         }
7658
7659         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7660         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7661         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7662         Store[x][y] = 0;
7663       }
7664     }
7665     else if ((CAN_PASS_MAGIC_WALL(element) &&
7666               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7667                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7668              (CAN_PASS_DC_MAGIC_WALL(element) &&
7669               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7670
7671     {
7672       InitMovingField(x, y, MV_DOWN);
7673       started_moving = TRUE;
7674
7675       Feld[x][y] =
7676         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7677          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7678          EL_DC_MAGIC_WALL_FILLING);
7679       Store[x][y] = element;
7680     }
7681     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7682     {
7683       SplashAcid(x, y + 1);
7684
7685       InitMovingField(x, y, MV_DOWN);
7686       started_moving = TRUE;
7687
7688       Store[x][y] = EL_ACID;
7689     }
7690     else if (
7691              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7692               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7693              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7694               CAN_FALL(element) && WasJustFalling[x][y] &&
7695               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7696
7697              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7698               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7699               (Feld[x][y + 1] == EL_BLOCKED)))
7700     {
7701       /* this is needed for a special case not covered by calling "Impact()"
7702          from "ContinueMoving()": if an element moves to a tile directly below
7703          another element which was just falling on that tile (which was empty
7704          in the previous frame), the falling element above would just stop
7705          instead of smashing the element below (in previous version, the above
7706          element was just checked for "moving" instead of "falling", resulting
7707          in incorrect smashes caused by horizontal movement of the above
7708          element; also, the case of the player being the element to smash was
7709          simply not covered here... :-/ ) */
7710
7711       CheckCollision[x][y] = 0;
7712       CheckImpact[x][y] = 0;
7713
7714       Impact(x, y);
7715     }
7716     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7717     {
7718       if (MovDir[x][y] == MV_NONE)
7719       {
7720         InitMovingField(x, y, MV_DOWN);
7721         started_moving = TRUE;
7722       }
7723     }
7724     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7725     {
7726       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7727         MovDir[x][y] = MV_DOWN;
7728
7729       InitMovingField(x, y, MV_DOWN);
7730       started_moving = TRUE;
7731     }
7732     else if (element == EL_AMOEBA_DROP)
7733     {
7734       Feld[x][y] = EL_AMOEBA_GROWING;
7735       Store[x][y] = EL_AMOEBA_WET;
7736     }
7737     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7738               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7739              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7740              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7741     {
7742       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7743                                 (IS_FREE(x - 1, y + 1) ||
7744                                  Feld[x - 1][y + 1] == EL_ACID));
7745       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7746                                 (IS_FREE(x + 1, y + 1) ||
7747                                  Feld[x + 1][y + 1] == EL_ACID));
7748       boolean can_fall_any  = (can_fall_left || can_fall_right);
7749       boolean can_fall_both = (can_fall_left && can_fall_right);
7750       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7751
7752       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7753       {
7754         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7755           can_fall_right = FALSE;
7756         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7757           can_fall_left = FALSE;
7758         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7759           can_fall_right = FALSE;
7760         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7761           can_fall_left = FALSE;
7762
7763         can_fall_any  = (can_fall_left || can_fall_right);
7764         can_fall_both = FALSE;
7765       }
7766
7767       if (can_fall_both)
7768       {
7769         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7770           can_fall_right = FALSE;       /* slip down on left side */
7771         else
7772           can_fall_left = !(can_fall_right = RND(2));
7773
7774         can_fall_both = FALSE;
7775       }
7776
7777       if (can_fall_any)
7778       {
7779         /* if not determined otherwise, prefer left side for slipping down */
7780         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7781         started_moving = TRUE;
7782       }
7783     }
7784     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7785     {
7786       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7787       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7788       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7789       int belt_dir = game.belt_dir[belt_nr];
7790
7791       if ((belt_dir == MV_LEFT  && left_is_free) ||
7792           (belt_dir == MV_RIGHT && right_is_free))
7793       {
7794         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7795
7796         InitMovingField(x, y, belt_dir);
7797         started_moving = TRUE;
7798
7799         Pushed[x][y] = TRUE;
7800         Pushed[nextx][y] = TRUE;
7801
7802         GfxAction[x][y] = ACTION_DEFAULT;
7803       }
7804       else
7805       {
7806         MovDir[x][y] = 0;       /* if element was moving, stop it */
7807       }
7808     }
7809   }
7810
7811   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7812   if (CAN_MOVE(element) && !started_moving)
7813   {
7814     int move_pattern = element_info[element].move_pattern;
7815     int newx, newy;
7816
7817     Moving2Blocked(x, y, &newx, &newy);
7818
7819     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7820       return;
7821
7822     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7823         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7824     {
7825       WasJustMoving[x][y] = 0;
7826       CheckCollision[x][y] = 0;
7827
7828       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7829
7830       if (Feld[x][y] != element)        /* element has changed */
7831         return;
7832     }
7833
7834     if (!MovDelay[x][y])        /* start new movement phase */
7835     {
7836       /* all objects that can change their move direction after each step
7837          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7838
7839       if (element != EL_YAMYAM &&
7840           element != EL_DARK_YAMYAM &&
7841           element != EL_PACMAN &&
7842           !(move_pattern & MV_ANY_DIRECTION) &&
7843           move_pattern != MV_TURNING_LEFT &&
7844           move_pattern != MV_TURNING_RIGHT &&
7845           move_pattern != MV_TURNING_LEFT_RIGHT &&
7846           move_pattern != MV_TURNING_RIGHT_LEFT &&
7847           move_pattern != MV_TURNING_RANDOM)
7848       {
7849         TurnRound(x, y);
7850
7851         if (MovDelay[x][y] && (element == EL_BUG ||
7852                                element == EL_SPACESHIP ||
7853                                element == EL_SP_SNIKSNAK ||
7854                                element == EL_SP_ELECTRON ||
7855                                element == EL_MOLE))
7856           TEST_DrawLevelField(x, y);
7857       }
7858     }
7859
7860     if (MovDelay[x][y])         /* wait some time before next movement */
7861     {
7862       MovDelay[x][y]--;
7863
7864       if (element == EL_ROBOT ||
7865           element == EL_YAMYAM ||
7866           element == EL_DARK_YAMYAM)
7867       {
7868         DrawLevelElementAnimationIfNeeded(x, y, element);
7869         PlayLevelSoundAction(x, y, ACTION_WAITING);
7870       }
7871       else if (element == EL_SP_ELECTRON)
7872         DrawLevelElementAnimationIfNeeded(x, y, element);
7873       else if (element == EL_DRAGON)
7874       {
7875         int i;
7876         int dir = MovDir[x][y];
7877         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7878         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7879         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7880                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7881                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7882                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7883         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7884
7885         GfxAction[x][y] = ACTION_ATTACKING;
7886
7887         if (IS_PLAYER(x, y))
7888           DrawPlayerField(x, y);
7889         else
7890           TEST_DrawLevelField(x, y);
7891
7892         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7893
7894         for (i = 1; i <= 3; i++)
7895         {
7896           int xx = x + i * dx;
7897           int yy = y + i * dy;
7898           int sx = SCREENX(xx);
7899           int sy = SCREENY(yy);
7900           int flame_graphic = graphic + (i - 1);
7901
7902           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7903             break;
7904
7905           if (MovDelay[x][y])
7906           {
7907             int flamed = MovingOrBlocked2Element(xx, yy);
7908
7909             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7910               Bang(xx, yy);
7911             else
7912               RemoveMovingField(xx, yy);
7913
7914             ChangeDelay[xx][yy] = 0;
7915
7916             Feld[xx][yy] = EL_FLAMES;
7917
7918             if (IN_SCR_FIELD(sx, sy))
7919             {
7920               TEST_DrawLevelFieldCrumbled(xx, yy);
7921               DrawGraphic(sx, sy, flame_graphic, frame);
7922             }
7923           }
7924           else
7925           {
7926             if (Feld[xx][yy] == EL_FLAMES)
7927               Feld[xx][yy] = EL_EMPTY;
7928             TEST_DrawLevelField(xx, yy);
7929           }
7930         }
7931       }
7932
7933       if (MovDelay[x][y])       /* element still has to wait some time */
7934       {
7935         PlayLevelSoundAction(x, y, ACTION_WAITING);
7936
7937         return;
7938       }
7939     }
7940
7941     /* now make next step */
7942
7943     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7944
7945     if (DONT_COLLIDE_WITH(element) &&
7946         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7947         !PLAYER_ENEMY_PROTECTED(newx, newy))
7948     {
7949       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7950
7951       return;
7952     }
7953
7954     else if (CAN_MOVE_INTO_ACID(element) &&
7955              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7956              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7957              (MovDir[x][y] == MV_DOWN ||
7958               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7959     {
7960       SplashAcid(newx, newy);
7961       Store[x][y] = EL_ACID;
7962     }
7963     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7964     {
7965       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7966           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7967           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7968           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7969       {
7970         RemoveField(x, y);
7971         TEST_DrawLevelField(x, y);
7972
7973         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7974         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7975           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7976
7977         local_player->friends_still_needed--;
7978         if (!local_player->friends_still_needed &&
7979             !local_player->GameOver && AllPlayersGone)
7980           PlayerWins(local_player);
7981
7982         return;
7983       }
7984       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7985       {
7986         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7987           TEST_DrawLevelField(newx, newy);
7988         else
7989           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7990       }
7991       else if (!IS_FREE(newx, newy))
7992       {
7993         GfxAction[x][y] = ACTION_WAITING;
7994
7995         if (IS_PLAYER(x, y))
7996           DrawPlayerField(x, y);
7997         else
7998           TEST_DrawLevelField(x, y);
7999
8000         return;
8001       }
8002     }
8003     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8004     {
8005       if (IS_FOOD_PIG(Feld[newx][newy]))
8006       {
8007         if (IS_MOVING(newx, newy))
8008           RemoveMovingField(newx, newy);
8009         else
8010         {
8011           Feld[newx][newy] = EL_EMPTY;
8012           TEST_DrawLevelField(newx, newy);
8013         }
8014
8015         PlayLevelSound(x, y, SND_PIG_DIGGING);
8016       }
8017       else if (!IS_FREE(newx, newy))
8018       {
8019         if (IS_PLAYER(x, y))
8020           DrawPlayerField(x, y);
8021         else
8022           TEST_DrawLevelField(x, y);
8023
8024         return;
8025       }
8026     }
8027     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8028     {
8029       if (Store[x][y] != EL_EMPTY)
8030       {
8031         boolean can_clone = FALSE;
8032         int xx, yy;
8033
8034         /* check if element to clone is still there */
8035         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8036         {
8037           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8038           {
8039             can_clone = TRUE;
8040
8041             break;
8042           }
8043         }
8044
8045         /* cannot clone or target field not free anymore -- do not clone */
8046         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8047           Store[x][y] = EL_EMPTY;
8048       }
8049
8050       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8051       {
8052         if (IS_MV_DIAGONAL(MovDir[x][y]))
8053         {
8054           int diagonal_move_dir = MovDir[x][y];
8055           int stored = Store[x][y];
8056           int change_delay = 8;
8057           int graphic;
8058
8059           /* android is moving diagonally */
8060
8061           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8062
8063           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8064           GfxElement[x][y] = EL_EMC_ANDROID;
8065           GfxAction[x][y] = ACTION_SHRINKING;
8066           GfxDir[x][y] = diagonal_move_dir;
8067           ChangeDelay[x][y] = change_delay;
8068
8069           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8070                                    GfxDir[x][y]);
8071
8072           DrawLevelGraphicAnimation(x, y, graphic);
8073           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8074
8075           if (Feld[newx][newy] == EL_ACID)
8076           {
8077             SplashAcid(newx, newy);
8078
8079             return;
8080           }
8081
8082           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8083
8084           Store[newx][newy] = EL_EMC_ANDROID;
8085           GfxElement[newx][newy] = EL_EMC_ANDROID;
8086           GfxAction[newx][newy] = ACTION_GROWING;
8087           GfxDir[newx][newy] = diagonal_move_dir;
8088           ChangeDelay[newx][newy] = change_delay;
8089
8090           graphic = el_act_dir2img(GfxElement[newx][newy],
8091                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8092
8093           DrawLevelGraphicAnimation(newx, newy, graphic);
8094           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8095
8096           return;
8097         }
8098         else
8099         {
8100           Feld[newx][newy] = EL_EMPTY;
8101           TEST_DrawLevelField(newx, newy);
8102
8103           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8104         }
8105       }
8106       else if (!IS_FREE(newx, newy))
8107       {
8108         return;
8109       }
8110     }
8111     else if (IS_CUSTOM_ELEMENT(element) &&
8112              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8113     {
8114       if (!DigFieldByCE(newx, newy, element))
8115         return;
8116
8117       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8118       {
8119         RunnerVisit[x][y] = FrameCounter;
8120         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8121       }
8122     }
8123     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8124     {
8125       if (!IS_FREE(newx, newy))
8126       {
8127         if (IS_PLAYER(x, y))
8128           DrawPlayerField(x, y);
8129         else
8130           TEST_DrawLevelField(x, y);
8131
8132         return;
8133       }
8134       else
8135       {
8136         boolean wanna_flame = !RND(10);
8137         int dx = newx - x, dy = newy - y;
8138         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8139         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8140         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8141                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8142         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8143                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8144
8145         if ((wanna_flame ||
8146              IS_CLASSIC_ENEMY(element1) ||
8147              IS_CLASSIC_ENEMY(element2)) &&
8148             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8149             element1 != EL_FLAMES && element2 != EL_FLAMES)
8150         {
8151           ResetGfxAnimation(x, y);
8152           GfxAction[x][y] = ACTION_ATTACKING;
8153
8154           if (IS_PLAYER(x, y))
8155             DrawPlayerField(x, y);
8156           else
8157             TEST_DrawLevelField(x, y);
8158
8159           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8160
8161           MovDelay[x][y] = 50;
8162
8163           Feld[newx][newy] = EL_FLAMES;
8164           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8165             Feld[newx1][newy1] = EL_FLAMES;
8166           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8167             Feld[newx2][newy2] = EL_FLAMES;
8168
8169           return;
8170         }
8171       }
8172     }
8173     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8174              Feld[newx][newy] == EL_DIAMOND)
8175     {
8176       if (IS_MOVING(newx, newy))
8177         RemoveMovingField(newx, newy);
8178       else
8179       {
8180         Feld[newx][newy] = EL_EMPTY;
8181         TEST_DrawLevelField(newx, newy);
8182       }
8183
8184       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8185     }
8186     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8187              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8188     {
8189       if (AmoebaNr[newx][newy])
8190       {
8191         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8192         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8193             Feld[newx][newy] == EL_BD_AMOEBA)
8194           AmoebaCnt[AmoebaNr[newx][newy]]--;
8195       }
8196
8197       if (IS_MOVING(newx, newy))
8198       {
8199         RemoveMovingField(newx, newy);
8200       }
8201       else
8202       {
8203         Feld[newx][newy] = EL_EMPTY;
8204         TEST_DrawLevelField(newx, newy);
8205       }
8206
8207       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8208     }
8209     else if ((element == EL_PACMAN || element == EL_MOLE)
8210              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8211     {
8212       if (AmoebaNr[newx][newy])
8213       {
8214         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8215         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8216             Feld[newx][newy] == EL_BD_AMOEBA)
8217           AmoebaCnt[AmoebaNr[newx][newy]]--;
8218       }
8219
8220       if (element == EL_MOLE)
8221       {
8222         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8223         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8224
8225         ResetGfxAnimation(x, y);
8226         GfxAction[x][y] = ACTION_DIGGING;
8227         TEST_DrawLevelField(x, y);
8228
8229         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8230
8231         return;                         /* wait for shrinking amoeba */
8232       }
8233       else      /* element == EL_PACMAN */
8234       {
8235         Feld[newx][newy] = EL_EMPTY;
8236         TEST_DrawLevelField(newx, newy);
8237         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8238       }
8239     }
8240     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8241              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8242               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8243     {
8244       /* wait for shrinking amoeba to completely disappear */
8245       return;
8246     }
8247     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8248     {
8249       /* object was running against a wall */
8250
8251       TurnRound(x, y);
8252
8253       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8254         DrawLevelElementAnimation(x, y, element);
8255
8256       if (DONT_TOUCH(element))
8257         TestIfBadThingTouchesPlayer(x, y);
8258
8259       return;
8260     }
8261
8262     InitMovingField(x, y, MovDir[x][y]);
8263
8264     PlayLevelSoundAction(x, y, ACTION_MOVING);
8265   }
8266
8267   if (MovDir[x][y])
8268     ContinueMoving(x, y);
8269 }
8270
8271 void ContinueMoving(int x, int y)
8272 {
8273   int element = Feld[x][y];
8274   struct ElementInfo *ei = &element_info[element];
8275   int direction = MovDir[x][y];
8276   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8277   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8278   int newx = x + dx, newy = y + dy;
8279   int stored = Store[x][y];
8280   int stored_new = Store[newx][newy];
8281   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8282   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8283   boolean last_line = (newy == lev_fieldy - 1);
8284
8285   MovPos[x][y] += getElementMoveStepsize(x, y);
8286
8287   if (pushed_by_player) /* special case: moving object pushed by player */
8288     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8289
8290   if (ABS(MovPos[x][y]) < TILEX)
8291   {
8292     TEST_DrawLevelField(x, y);
8293
8294     return;     /* element is still moving */
8295   }
8296
8297   /* element reached destination field */
8298
8299   Feld[x][y] = EL_EMPTY;
8300   Feld[newx][newy] = element;
8301   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8302
8303   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8304   {
8305     element = Feld[newx][newy] = EL_ACID;
8306   }
8307   else if (element == EL_MOLE)
8308   {
8309     Feld[x][y] = EL_SAND;
8310
8311     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8312   }
8313   else if (element == EL_QUICKSAND_FILLING)
8314   {
8315     element = Feld[newx][newy] = get_next_element(element);
8316     Store[newx][newy] = Store[x][y];
8317   }
8318   else if (element == EL_QUICKSAND_EMPTYING)
8319   {
8320     Feld[x][y] = get_next_element(element);
8321     element = Feld[newx][newy] = Store[x][y];
8322   }
8323   else if (element == EL_QUICKSAND_FAST_FILLING)
8324   {
8325     element = Feld[newx][newy] = get_next_element(element);
8326     Store[newx][newy] = Store[x][y];
8327   }
8328   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8329   {
8330     Feld[x][y] = get_next_element(element);
8331     element = Feld[newx][newy] = Store[x][y];
8332   }
8333   else if (element == EL_MAGIC_WALL_FILLING)
8334   {
8335     element = Feld[newx][newy] = get_next_element(element);
8336     if (!game.magic_wall_active)
8337       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8338     Store[newx][newy] = Store[x][y];
8339   }
8340   else if (element == EL_MAGIC_WALL_EMPTYING)
8341   {
8342     Feld[x][y] = get_next_element(element);
8343     if (!game.magic_wall_active)
8344       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8345     element = Feld[newx][newy] = Store[x][y];
8346
8347     InitField(newx, newy, FALSE);
8348   }
8349   else if (element == EL_BD_MAGIC_WALL_FILLING)
8350   {
8351     element = Feld[newx][newy] = get_next_element(element);
8352     if (!game.magic_wall_active)
8353       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8354     Store[newx][newy] = Store[x][y];
8355   }
8356   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8357   {
8358     Feld[x][y] = get_next_element(element);
8359     if (!game.magic_wall_active)
8360       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8361     element = Feld[newx][newy] = Store[x][y];
8362
8363     InitField(newx, newy, FALSE);
8364   }
8365   else if (element == EL_DC_MAGIC_WALL_FILLING)
8366   {
8367     element = Feld[newx][newy] = get_next_element(element);
8368     if (!game.magic_wall_active)
8369       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8370     Store[newx][newy] = Store[x][y];
8371   }
8372   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8373   {
8374     Feld[x][y] = get_next_element(element);
8375     if (!game.magic_wall_active)
8376       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8377     element = Feld[newx][newy] = Store[x][y];
8378
8379     InitField(newx, newy, FALSE);
8380   }
8381   else if (element == EL_AMOEBA_DROPPING)
8382   {
8383     Feld[x][y] = get_next_element(element);
8384     element = Feld[newx][newy] = Store[x][y];
8385   }
8386   else if (element == EL_SOKOBAN_OBJECT)
8387   {
8388     if (Back[x][y])
8389       Feld[x][y] = Back[x][y];
8390
8391     if (Back[newx][newy])
8392       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8393
8394     Back[x][y] = Back[newx][newy] = 0;
8395   }
8396
8397   Store[x][y] = EL_EMPTY;
8398   MovPos[x][y] = 0;
8399   MovDir[x][y] = 0;
8400   MovDelay[x][y] = 0;
8401
8402   MovDelay[newx][newy] = 0;
8403
8404   if (CAN_CHANGE_OR_HAS_ACTION(element))
8405   {
8406     /* copy element change control values to new field */
8407     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8408     ChangePage[newx][newy]  = ChangePage[x][y];
8409     ChangeCount[newx][newy] = ChangeCount[x][y];
8410     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8411   }
8412
8413   CustomValue[newx][newy] = CustomValue[x][y];
8414
8415   ChangeDelay[x][y] = 0;
8416   ChangePage[x][y] = -1;
8417   ChangeCount[x][y] = 0;
8418   ChangeEvent[x][y] = -1;
8419
8420   CustomValue[x][y] = 0;
8421
8422   /* copy animation control values to new field */
8423   GfxFrame[newx][newy]  = GfxFrame[x][y];
8424   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8425   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8426   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8427
8428   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8429
8430   /* some elements can leave other elements behind after moving */
8431   if (ei->move_leave_element != EL_EMPTY &&
8432       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8433       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8434   {
8435     int move_leave_element = ei->move_leave_element;
8436
8437     /* this makes it possible to leave the removed element again */
8438     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8439       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8440
8441     Feld[x][y] = move_leave_element;
8442
8443     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8444       MovDir[x][y] = direction;
8445
8446     InitField(x, y, FALSE);
8447
8448     if (GFX_CRUMBLED(Feld[x][y]))
8449       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8450
8451     if (ELEM_IS_PLAYER(move_leave_element))
8452       RelocatePlayer(x, y, move_leave_element);
8453   }
8454
8455   /* do this after checking for left-behind element */
8456   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8457
8458   if (!CAN_MOVE(element) ||
8459       (CAN_FALL(element) && direction == MV_DOWN &&
8460        (element == EL_SPRING ||
8461         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8462         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8463     GfxDir[x][y] = MovDir[newx][newy] = 0;
8464
8465   TEST_DrawLevelField(x, y);
8466   TEST_DrawLevelField(newx, newy);
8467
8468   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8469
8470   /* prevent pushed element from moving on in pushed direction */
8471   if (pushed_by_player && CAN_MOVE(element) &&
8472       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8473       !(element_info[element].move_pattern & direction))
8474     TurnRound(newx, newy);
8475
8476   /* prevent elements on conveyor belt from moving on in last direction */
8477   if (pushed_by_conveyor && CAN_FALL(element) &&
8478       direction & MV_HORIZONTAL)
8479     MovDir[newx][newy] = 0;
8480
8481   if (!pushed_by_player)
8482   {
8483     int nextx = newx + dx, nexty = newy + dy;
8484     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8485
8486     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8487
8488     if (CAN_FALL(element) && direction == MV_DOWN)
8489       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8490
8491     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8492       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8493
8494     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8495       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8496   }
8497
8498   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8499   {
8500     TestIfBadThingTouchesPlayer(newx, newy);
8501     TestIfBadThingTouchesFriend(newx, newy);
8502
8503     if (!IS_CUSTOM_ELEMENT(element))
8504       TestIfBadThingTouchesOtherBadThing(newx, newy);
8505   }
8506   else if (element == EL_PENGUIN)
8507     TestIfFriendTouchesBadThing(newx, newy);
8508
8509   if (DONT_GET_HIT_BY(element))
8510   {
8511     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8512   }
8513
8514   /* give the player one last chance (one more frame) to move away */
8515   if (CAN_FALL(element) && direction == MV_DOWN &&
8516       (last_line || (!IS_FREE(x, newy + 1) &&
8517                      (!IS_PLAYER(x, newy + 1) ||
8518                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8519     Impact(x, newy);
8520
8521   if (pushed_by_player && !game.use_change_when_pushing_bug)
8522   {
8523     int push_side = MV_DIR_OPPOSITE(direction);
8524     struct PlayerInfo *player = PLAYERINFO(x, y);
8525
8526     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8527                                player->index_bit, push_side);
8528     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8529                                         player->index_bit, push_side);
8530   }
8531
8532   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8533     MovDelay[newx][newy] = 1;
8534
8535   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8536
8537   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8538   TestIfElementHitsCustomElement(newx, newy, direction);
8539   TestIfPlayerTouchesCustomElement(newx, newy);
8540   TestIfElementTouchesCustomElement(newx, newy);
8541
8542   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8543       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8544     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8545                              MV_DIR_OPPOSITE(direction));
8546 }
8547
8548 int AmoebeNachbarNr(int ax, int ay)
8549 {
8550   int i;
8551   int element = Feld[ax][ay];
8552   int group_nr = 0;
8553   static int xy[4][2] =
8554   {
8555     { 0, -1 },
8556     { -1, 0 },
8557     { +1, 0 },
8558     { 0, +1 }
8559   };
8560
8561   for (i = 0; i < NUM_DIRECTIONS; i++)
8562   {
8563     int x = ax + xy[i][0];
8564     int y = ay + xy[i][1];
8565
8566     if (!IN_LEV_FIELD(x, y))
8567       continue;
8568
8569     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8570       group_nr = AmoebaNr[x][y];
8571   }
8572
8573   return group_nr;
8574 }
8575
8576 void AmoebenVereinigen(int ax, int ay)
8577 {
8578   int i, x, y, xx, yy;
8579   int new_group_nr = AmoebaNr[ax][ay];
8580   static int xy[4][2] =
8581   {
8582     { 0, -1 },
8583     { -1, 0 },
8584     { +1, 0 },
8585     { 0, +1 }
8586   };
8587
8588   if (new_group_nr == 0)
8589     return;
8590
8591   for (i = 0; i < NUM_DIRECTIONS; i++)
8592   {
8593     x = ax + xy[i][0];
8594     y = ay + xy[i][1];
8595
8596     if (!IN_LEV_FIELD(x, y))
8597       continue;
8598
8599     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8600          Feld[x][y] == EL_BD_AMOEBA ||
8601          Feld[x][y] == EL_AMOEBA_DEAD) &&
8602         AmoebaNr[x][y] != new_group_nr)
8603     {
8604       int old_group_nr = AmoebaNr[x][y];
8605
8606       if (old_group_nr == 0)
8607         return;
8608
8609       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8610       AmoebaCnt[old_group_nr] = 0;
8611       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8612       AmoebaCnt2[old_group_nr] = 0;
8613
8614       SCAN_PLAYFIELD(xx, yy)
8615       {
8616         if (AmoebaNr[xx][yy] == old_group_nr)
8617           AmoebaNr[xx][yy] = new_group_nr;
8618       }
8619     }
8620   }
8621 }
8622
8623 void AmoebeUmwandeln(int ax, int ay)
8624 {
8625   int i, x, y;
8626
8627   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8628   {
8629     int group_nr = AmoebaNr[ax][ay];
8630
8631 #ifdef DEBUG
8632     if (group_nr == 0)
8633     {
8634       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8635       printf("AmoebeUmwandeln(): This should never happen!\n");
8636       return;
8637     }
8638 #endif
8639
8640     SCAN_PLAYFIELD(x, y)
8641     {
8642       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8643       {
8644         AmoebaNr[x][y] = 0;
8645         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8646       }
8647     }
8648
8649     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8650                             SND_AMOEBA_TURNING_TO_GEM :
8651                             SND_AMOEBA_TURNING_TO_ROCK));
8652     Bang(ax, ay);
8653   }
8654   else
8655   {
8656     static int xy[4][2] =
8657     {
8658       { 0, -1 },
8659       { -1, 0 },
8660       { +1, 0 },
8661       { 0, +1 }
8662     };
8663
8664     for (i = 0; i < NUM_DIRECTIONS; i++)
8665     {
8666       x = ax + xy[i][0];
8667       y = ay + xy[i][1];
8668
8669       if (!IN_LEV_FIELD(x, y))
8670         continue;
8671
8672       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8673       {
8674         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8675                               SND_AMOEBA_TURNING_TO_GEM :
8676                               SND_AMOEBA_TURNING_TO_ROCK));
8677         Bang(x, y);
8678       }
8679     }
8680   }
8681 }
8682
8683 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8684 {
8685   int x, y;
8686   int group_nr = AmoebaNr[ax][ay];
8687   boolean done = FALSE;
8688
8689 #ifdef DEBUG
8690   if (group_nr == 0)
8691   {
8692     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8693     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8694     return;
8695   }
8696 #endif
8697
8698   SCAN_PLAYFIELD(x, y)
8699   {
8700     if (AmoebaNr[x][y] == group_nr &&
8701         (Feld[x][y] == EL_AMOEBA_DEAD ||
8702          Feld[x][y] == EL_BD_AMOEBA ||
8703          Feld[x][y] == EL_AMOEBA_GROWING))
8704     {
8705       AmoebaNr[x][y] = 0;
8706       Feld[x][y] = new_element;
8707       InitField(x, y, FALSE);
8708       TEST_DrawLevelField(x, y);
8709       done = TRUE;
8710     }
8711   }
8712
8713   if (done)
8714     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8715                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8716                             SND_BD_AMOEBA_TURNING_TO_GEM));
8717 }
8718
8719 void AmoebeWaechst(int x, int y)
8720 {
8721   static unsigned int sound_delay = 0;
8722   static unsigned int sound_delay_value = 0;
8723
8724   if (!MovDelay[x][y])          /* start new growing cycle */
8725   {
8726     MovDelay[x][y] = 7;
8727
8728     if (DelayReached(&sound_delay, sound_delay_value))
8729     {
8730       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8731       sound_delay_value = 30;
8732     }
8733   }
8734
8735   if (MovDelay[x][y])           /* wait some time before growing bigger */
8736   {
8737     MovDelay[x][y]--;
8738     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8739     {
8740       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8741                                            6 - MovDelay[x][y]);
8742
8743       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8744     }
8745
8746     if (!MovDelay[x][y])
8747     {
8748       Feld[x][y] = Store[x][y];
8749       Store[x][y] = 0;
8750       TEST_DrawLevelField(x, y);
8751     }
8752   }
8753 }
8754
8755 void AmoebaDisappearing(int x, int y)
8756 {
8757   static unsigned int sound_delay = 0;
8758   static unsigned int sound_delay_value = 0;
8759
8760   if (!MovDelay[x][y])          /* start new shrinking cycle */
8761   {
8762     MovDelay[x][y] = 7;
8763
8764     if (DelayReached(&sound_delay, sound_delay_value))
8765       sound_delay_value = 30;
8766   }
8767
8768   if (MovDelay[x][y])           /* wait some time before shrinking */
8769   {
8770     MovDelay[x][y]--;
8771     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8772     {
8773       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8774                                            6 - MovDelay[x][y]);
8775
8776       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8777     }
8778
8779     if (!MovDelay[x][y])
8780     {
8781       Feld[x][y] = EL_EMPTY;
8782       TEST_DrawLevelField(x, y);
8783
8784       /* don't let mole enter this field in this cycle;
8785          (give priority to objects falling to this field from above) */
8786       Stop[x][y] = TRUE;
8787     }
8788   }
8789 }
8790
8791 void AmoebeAbleger(int ax, int ay)
8792 {
8793   int i;
8794   int element = Feld[ax][ay];
8795   int graphic = el2img(element);
8796   int newax = ax, neway = ay;
8797   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8798   static int xy[4][2] =
8799   {
8800     { 0, -1 },
8801     { -1, 0 },
8802     { +1, 0 },
8803     { 0, +1 }
8804   };
8805
8806   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8807   {
8808     Feld[ax][ay] = EL_AMOEBA_DEAD;
8809     TEST_DrawLevelField(ax, ay);
8810     return;
8811   }
8812
8813   if (IS_ANIMATED(graphic))
8814     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8815
8816   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8817     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8818
8819   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8820   {
8821     MovDelay[ax][ay]--;
8822     if (MovDelay[ax][ay])
8823       return;
8824   }
8825
8826   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8827   {
8828     int start = RND(4);
8829     int x = ax + xy[start][0];
8830     int y = ay + xy[start][1];
8831
8832     if (!IN_LEV_FIELD(x, y))
8833       return;
8834
8835     if (IS_FREE(x, y) ||
8836         CAN_GROW_INTO(Feld[x][y]) ||
8837         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8838         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8839     {
8840       newax = x;
8841       neway = y;
8842     }
8843
8844     if (newax == ax && neway == ay)
8845       return;
8846   }
8847   else                          /* normal or "filled" (BD style) amoeba */
8848   {
8849     int start = RND(4);
8850     boolean waiting_for_player = FALSE;
8851
8852     for (i = 0; i < NUM_DIRECTIONS; i++)
8853     {
8854       int j = (start + i) % 4;
8855       int x = ax + xy[j][0];
8856       int y = ay + xy[j][1];
8857
8858       if (!IN_LEV_FIELD(x, y))
8859         continue;
8860
8861       if (IS_FREE(x, y) ||
8862           CAN_GROW_INTO(Feld[x][y]) ||
8863           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8864           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8865       {
8866         newax = x;
8867         neway = y;
8868         break;
8869       }
8870       else if (IS_PLAYER(x, y))
8871         waiting_for_player = TRUE;
8872     }
8873
8874     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8875     {
8876       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8877       {
8878         Feld[ax][ay] = EL_AMOEBA_DEAD;
8879         TEST_DrawLevelField(ax, ay);
8880         AmoebaCnt[AmoebaNr[ax][ay]]--;
8881
8882         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8883         {
8884           if (element == EL_AMOEBA_FULL)
8885             AmoebeUmwandeln(ax, ay);
8886           else if (element == EL_BD_AMOEBA)
8887             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8888         }
8889       }
8890       return;
8891     }
8892     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8893     {
8894       /* amoeba gets larger by growing in some direction */
8895
8896       int new_group_nr = AmoebaNr[ax][ay];
8897
8898 #ifdef DEBUG
8899   if (new_group_nr == 0)
8900   {
8901     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8902     printf("AmoebeAbleger(): This should never happen!\n");
8903     return;
8904   }
8905 #endif
8906
8907       AmoebaNr[newax][neway] = new_group_nr;
8908       AmoebaCnt[new_group_nr]++;
8909       AmoebaCnt2[new_group_nr]++;
8910
8911       /* if amoeba touches other amoeba(s) after growing, unify them */
8912       AmoebenVereinigen(newax, neway);
8913
8914       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8915       {
8916         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8917         return;
8918       }
8919     }
8920   }
8921
8922   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8923       (neway == lev_fieldy - 1 && newax != ax))
8924   {
8925     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8926     Store[newax][neway] = element;
8927   }
8928   else if (neway == ay || element == EL_EMC_DRIPPER)
8929   {
8930     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8931
8932     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8933   }
8934   else
8935   {
8936     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8937     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8938     Store[ax][ay] = EL_AMOEBA_DROP;
8939     ContinueMoving(ax, ay);
8940     return;
8941   }
8942
8943   TEST_DrawLevelField(newax, neway);
8944 }
8945
8946 void Life(int ax, int ay)
8947 {
8948   int x1, y1, x2, y2;
8949   int life_time = 40;
8950   int element = Feld[ax][ay];
8951   int graphic = el2img(element);
8952   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8953                          level.biomaze);
8954   boolean changed = FALSE;
8955
8956   if (IS_ANIMATED(graphic))
8957     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8958
8959   if (Stop[ax][ay])
8960     return;
8961
8962   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8963     MovDelay[ax][ay] = life_time;
8964
8965   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8966   {
8967     MovDelay[ax][ay]--;
8968     if (MovDelay[ax][ay])
8969       return;
8970   }
8971
8972   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8973   {
8974     int xx = ax+x1, yy = ay+y1;
8975     int nachbarn = 0;
8976
8977     if (!IN_LEV_FIELD(xx, yy))
8978       continue;
8979
8980     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8981     {
8982       int x = xx+x2, y = yy+y2;
8983
8984       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8985         continue;
8986
8987       if (((Feld[x][y] == element ||
8988             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8989            !Stop[x][y]) ||
8990           (IS_FREE(x, y) && Stop[x][y]))
8991         nachbarn++;
8992     }
8993
8994     if (xx == ax && yy == ay)           /* field in the middle */
8995     {
8996       if (nachbarn < life_parameter[0] ||
8997           nachbarn > life_parameter[1])
8998       {
8999         Feld[xx][yy] = EL_EMPTY;
9000         if (!Stop[xx][yy])
9001           TEST_DrawLevelField(xx, yy);
9002         Stop[xx][yy] = TRUE;
9003         changed = TRUE;
9004       }
9005     }
9006     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9007     {                                   /* free border field */
9008       if (nachbarn >= life_parameter[2] &&
9009           nachbarn <= life_parameter[3])
9010       {
9011         Feld[xx][yy] = element;
9012         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9013         if (!Stop[xx][yy])
9014           TEST_DrawLevelField(xx, yy);
9015         Stop[xx][yy] = TRUE;
9016         changed = TRUE;
9017       }
9018     }
9019   }
9020
9021   if (changed)
9022     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9023                    SND_GAME_OF_LIFE_GROWING);
9024 }
9025
9026 static void InitRobotWheel(int x, int y)
9027 {
9028   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9029 }
9030
9031 static void RunRobotWheel(int x, int y)
9032 {
9033   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9034 }
9035
9036 static void StopRobotWheel(int x, int y)
9037 {
9038   if (ZX == x && ZY == y)
9039   {
9040     ZX = ZY = -1;
9041
9042     game.robot_wheel_active = FALSE;
9043   }
9044 }
9045
9046 static void InitTimegateWheel(int x, int y)
9047 {
9048   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9049 }
9050
9051 static void RunTimegateWheel(int x, int y)
9052 {
9053   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9054 }
9055
9056 static void InitMagicBallDelay(int x, int y)
9057 {
9058   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9059 }
9060
9061 static void ActivateMagicBall(int bx, int by)
9062 {
9063   int x, y;
9064
9065   if (level.ball_random)
9066   {
9067     int pos_border = RND(8);    /* select one of the eight border elements */
9068     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9069     int xx = pos_content % 3;
9070     int yy = pos_content / 3;
9071
9072     x = bx - 1 + xx;
9073     y = by - 1 + yy;
9074
9075     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9076       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9077   }
9078   else
9079   {
9080     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9081     {
9082       int xx = x - bx + 1;
9083       int yy = y - by + 1;
9084
9085       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9086         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9087     }
9088   }
9089
9090   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9091 }
9092
9093 void CheckExit(int x, int y)
9094 {
9095   if (local_player->gems_still_needed > 0 ||
9096       local_player->sokobanfields_still_needed > 0 ||
9097       local_player->lights_still_needed > 0)
9098   {
9099     int element = Feld[x][y];
9100     int graphic = el2img(element);
9101
9102     if (IS_ANIMATED(graphic))
9103       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9104
9105     return;
9106   }
9107
9108   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9109     return;
9110
9111   Feld[x][y] = EL_EXIT_OPENING;
9112
9113   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9114 }
9115
9116 void CheckExitEM(int x, int y)
9117 {
9118   if (local_player->gems_still_needed > 0 ||
9119       local_player->sokobanfields_still_needed > 0 ||
9120       local_player->lights_still_needed > 0)
9121   {
9122     int element = Feld[x][y];
9123     int graphic = el2img(element);
9124
9125     if (IS_ANIMATED(graphic))
9126       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9127
9128     return;
9129   }
9130
9131   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9132     return;
9133
9134   Feld[x][y] = EL_EM_EXIT_OPENING;
9135
9136   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9137 }
9138
9139 void CheckExitSteel(int x, int y)
9140 {
9141   if (local_player->gems_still_needed > 0 ||
9142       local_player->sokobanfields_still_needed > 0 ||
9143       local_player->lights_still_needed > 0)
9144   {
9145     int element = Feld[x][y];
9146     int graphic = el2img(element);
9147
9148     if (IS_ANIMATED(graphic))
9149       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9150
9151     return;
9152   }
9153
9154   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9155     return;
9156
9157   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9158
9159   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9160 }
9161
9162 void CheckExitSteelEM(int x, int y)
9163 {
9164   if (local_player->gems_still_needed > 0 ||
9165       local_player->sokobanfields_still_needed > 0 ||
9166       local_player->lights_still_needed > 0)
9167   {
9168     int element = Feld[x][y];
9169     int graphic = el2img(element);
9170
9171     if (IS_ANIMATED(graphic))
9172       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9173
9174     return;
9175   }
9176
9177   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9178     return;
9179
9180   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9181
9182   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9183 }
9184
9185 void CheckExitSP(int x, int y)
9186 {
9187   if (local_player->gems_still_needed > 0)
9188   {
9189     int element = Feld[x][y];
9190     int graphic = el2img(element);
9191
9192     if (IS_ANIMATED(graphic))
9193       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9194
9195     return;
9196   }
9197
9198   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9199     return;
9200
9201   Feld[x][y] = EL_SP_EXIT_OPENING;
9202
9203   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9204 }
9205
9206 static void CloseAllOpenTimegates()
9207 {
9208   int x, y;
9209
9210   SCAN_PLAYFIELD(x, y)
9211   {
9212     int element = Feld[x][y];
9213
9214     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9215     {
9216       Feld[x][y] = EL_TIMEGATE_CLOSING;
9217
9218       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9219     }
9220   }
9221 }
9222
9223 void DrawTwinkleOnField(int x, int y)
9224 {
9225   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9226     return;
9227
9228   if (Feld[x][y] == EL_BD_DIAMOND)
9229     return;
9230
9231   if (MovDelay[x][y] == 0)      /* next animation frame */
9232     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9233
9234   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9235   {
9236     MovDelay[x][y]--;
9237
9238     DrawLevelElementAnimation(x, y, Feld[x][y]);
9239
9240     if (MovDelay[x][y] != 0)
9241     {
9242       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9243                                            10 - MovDelay[x][y]);
9244
9245       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9246     }
9247   }
9248 }
9249
9250 void MauerWaechst(int x, int y)
9251 {
9252   int delay = 6;
9253
9254   if (!MovDelay[x][y])          /* next animation frame */
9255     MovDelay[x][y] = 3 * delay;
9256
9257   if (MovDelay[x][y])           /* wait some time before next frame */
9258   {
9259     MovDelay[x][y]--;
9260
9261     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9262     {
9263       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9264       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9265
9266       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9267     }
9268
9269     if (!MovDelay[x][y])
9270     {
9271       if (MovDir[x][y] == MV_LEFT)
9272       {
9273         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9274           TEST_DrawLevelField(x - 1, y);
9275       }
9276       else if (MovDir[x][y] == MV_RIGHT)
9277       {
9278         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9279           TEST_DrawLevelField(x + 1, y);
9280       }
9281       else if (MovDir[x][y] == MV_UP)
9282       {
9283         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9284           TEST_DrawLevelField(x, y - 1);
9285       }
9286       else
9287       {
9288         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9289           TEST_DrawLevelField(x, y + 1);
9290       }
9291
9292       Feld[x][y] = Store[x][y];
9293       Store[x][y] = 0;
9294       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9295       TEST_DrawLevelField(x, y);
9296     }
9297   }
9298 }
9299
9300 void MauerAbleger(int ax, int ay)
9301 {
9302   int element = Feld[ax][ay];
9303   int graphic = el2img(element);
9304   boolean oben_frei = FALSE, unten_frei = FALSE;
9305   boolean links_frei = FALSE, rechts_frei = FALSE;
9306   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9307   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9308   boolean new_wall = FALSE;
9309
9310   if (IS_ANIMATED(graphic))
9311     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9312
9313   if (!MovDelay[ax][ay])        /* start building new wall */
9314     MovDelay[ax][ay] = 6;
9315
9316   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9317   {
9318     MovDelay[ax][ay]--;
9319     if (MovDelay[ax][ay])
9320       return;
9321   }
9322
9323   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9324     oben_frei = TRUE;
9325   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9326     unten_frei = TRUE;
9327   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9328     links_frei = TRUE;
9329   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9330     rechts_frei = TRUE;
9331
9332   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9333       element == EL_EXPANDABLE_WALL_ANY)
9334   {
9335     if (oben_frei)
9336     {
9337       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9338       Store[ax][ay-1] = element;
9339       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9340       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9341         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9342                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9343       new_wall = TRUE;
9344     }
9345     if (unten_frei)
9346     {
9347       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9348       Store[ax][ay+1] = element;
9349       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9350       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9351         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9352                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9353       new_wall = TRUE;
9354     }
9355   }
9356
9357   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9358       element == EL_EXPANDABLE_WALL_ANY ||
9359       element == EL_EXPANDABLE_WALL ||
9360       element == EL_BD_EXPANDABLE_WALL)
9361   {
9362     if (links_frei)
9363     {
9364       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9365       Store[ax-1][ay] = element;
9366       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9367       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9368         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9369                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9370       new_wall = TRUE;
9371     }
9372
9373     if (rechts_frei)
9374     {
9375       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9376       Store[ax+1][ay] = element;
9377       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9378       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9379         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9380                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9381       new_wall = TRUE;
9382     }
9383   }
9384
9385   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9386     TEST_DrawLevelField(ax, ay);
9387
9388   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9389     oben_massiv = TRUE;
9390   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9391     unten_massiv = TRUE;
9392   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9393     links_massiv = TRUE;
9394   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9395     rechts_massiv = TRUE;
9396
9397   if (((oben_massiv && unten_massiv) ||
9398        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9399        element == EL_EXPANDABLE_WALL) &&
9400       ((links_massiv && rechts_massiv) ||
9401        element == EL_EXPANDABLE_WALL_VERTICAL))
9402     Feld[ax][ay] = EL_WALL;
9403
9404   if (new_wall)
9405     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9406 }
9407
9408 void MauerAblegerStahl(int ax, int ay)
9409 {
9410   int element = Feld[ax][ay];
9411   int graphic = el2img(element);
9412   boolean oben_frei = FALSE, unten_frei = FALSE;
9413   boolean links_frei = FALSE, rechts_frei = FALSE;
9414   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9415   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9416   boolean new_wall = FALSE;
9417
9418   if (IS_ANIMATED(graphic))
9419     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9420
9421   if (!MovDelay[ax][ay])        /* start building new wall */
9422     MovDelay[ax][ay] = 6;
9423
9424   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9425   {
9426     MovDelay[ax][ay]--;
9427     if (MovDelay[ax][ay])
9428       return;
9429   }
9430
9431   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9432     oben_frei = TRUE;
9433   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9434     unten_frei = TRUE;
9435   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9436     links_frei = TRUE;
9437   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9438     rechts_frei = TRUE;
9439
9440   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9441       element == EL_EXPANDABLE_STEELWALL_ANY)
9442   {
9443     if (oben_frei)
9444     {
9445       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9446       Store[ax][ay-1] = element;
9447       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9448       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9449         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9450                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9451       new_wall = TRUE;
9452     }
9453     if (unten_frei)
9454     {
9455       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9456       Store[ax][ay+1] = element;
9457       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9458       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9459         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9460                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9461       new_wall = TRUE;
9462     }
9463   }
9464
9465   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9466       element == EL_EXPANDABLE_STEELWALL_ANY)
9467   {
9468     if (links_frei)
9469     {
9470       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9471       Store[ax-1][ay] = element;
9472       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9473       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9474         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9475                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9476       new_wall = TRUE;
9477     }
9478
9479     if (rechts_frei)
9480     {
9481       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9482       Store[ax+1][ay] = element;
9483       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9484       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9485         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9486                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9487       new_wall = TRUE;
9488     }
9489   }
9490
9491   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9492     oben_massiv = TRUE;
9493   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9494     unten_massiv = TRUE;
9495   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9496     links_massiv = TRUE;
9497   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9498     rechts_massiv = TRUE;
9499
9500   if (((oben_massiv && unten_massiv) ||
9501        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9502       ((links_massiv && rechts_massiv) ||
9503        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9504     Feld[ax][ay] = EL_STEELWALL;
9505
9506   if (new_wall)
9507     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9508 }
9509
9510 void CheckForDragon(int x, int y)
9511 {
9512   int i, j;
9513   boolean dragon_found = FALSE;
9514   static int xy[4][2] =
9515   {
9516     { 0, -1 },
9517     { -1, 0 },
9518     { +1, 0 },
9519     { 0, +1 }
9520   };
9521
9522   for (i = 0; i < NUM_DIRECTIONS; i++)
9523   {
9524     for (j = 0; j < 4; j++)
9525     {
9526       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9527
9528       if (IN_LEV_FIELD(xx, yy) &&
9529           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9530       {
9531         if (Feld[xx][yy] == EL_DRAGON)
9532           dragon_found = TRUE;
9533       }
9534       else
9535         break;
9536     }
9537   }
9538
9539   if (!dragon_found)
9540   {
9541     for (i = 0; i < NUM_DIRECTIONS; i++)
9542     {
9543       for (j = 0; j < 3; j++)
9544       {
9545         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9546   
9547         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9548         {
9549           Feld[xx][yy] = EL_EMPTY;
9550           TEST_DrawLevelField(xx, yy);
9551         }
9552         else
9553           break;
9554       }
9555     }
9556   }
9557 }
9558
9559 static void InitBuggyBase(int x, int y)
9560 {
9561   int element = Feld[x][y];
9562   int activating_delay = FRAMES_PER_SECOND / 4;
9563
9564   ChangeDelay[x][y] =
9565     (element == EL_SP_BUGGY_BASE ?
9566      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9567      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9568      activating_delay :
9569      element == EL_SP_BUGGY_BASE_ACTIVE ?
9570      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9571 }
9572
9573 static void WarnBuggyBase(int x, int y)
9574 {
9575   int i;
9576   static int xy[4][2] =
9577   {
9578     { 0, -1 },
9579     { -1, 0 },
9580     { +1, 0 },
9581     { 0, +1 }
9582   };
9583
9584   for (i = 0; i < NUM_DIRECTIONS; i++)
9585   {
9586     int xx = x + xy[i][0];
9587     int yy = y + xy[i][1];
9588
9589     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9590     {
9591       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9592
9593       break;
9594     }
9595   }
9596 }
9597
9598 static void InitTrap(int x, int y)
9599 {
9600   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9601 }
9602
9603 static void ActivateTrap(int x, int y)
9604 {
9605   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9606 }
9607
9608 static void ChangeActiveTrap(int x, int y)
9609 {
9610   int graphic = IMG_TRAP_ACTIVE;
9611
9612   /* if new animation frame was drawn, correct crumbled sand border */
9613   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9614     TEST_DrawLevelFieldCrumbled(x, y);
9615 }
9616
9617 static int getSpecialActionElement(int element, int number, int base_element)
9618 {
9619   return (element != EL_EMPTY ? element :
9620           number != -1 ? base_element + number - 1 :
9621           EL_EMPTY);
9622 }
9623
9624 static int getModifiedActionNumber(int value_old, int operator, int operand,
9625                                    int value_min, int value_max)
9626 {
9627   int value_new = (operator == CA_MODE_SET      ? operand :
9628                    operator == CA_MODE_ADD      ? value_old + operand :
9629                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9630                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9631                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9632                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9633                    value_old);
9634
9635   return (value_new < value_min ? value_min :
9636           value_new > value_max ? value_max :
9637           value_new);
9638 }
9639
9640 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9641 {
9642   struct ElementInfo *ei = &element_info[element];
9643   struct ElementChangeInfo *change = &ei->change_page[page];
9644   int target_element = change->target_element;
9645   int action_type = change->action_type;
9646   int action_mode = change->action_mode;
9647   int action_arg = change->action_arg;
9648   int action_element = change->action_element;
9649   int i;
9650
9651   if (!change->has_action)
9652     return;
9653
9654   /* ---------- determine action paramater values -------------------------- */
9655
9656   int level_time_value =
9657     (level.time > 0 ? TimeLeft :
9658      TimePlayed);
9659
9660   int action_arg_element_raw =
9661     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9662      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9663      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9664      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9665      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9666      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9667      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9668      EL_EMPTY);
9669   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9670
9671   int action_arg_direction =
9672     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9673      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9674      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9675      change->actual_trigger_side :
9676      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9677      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9678      MV_NONE);
9679
9680   int action_arg_number_min =
9681     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9682      CA_ARG_MIN);
9683
9684   int action_arg_number_max =
9685     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9686      action_type == CA_SET_LEVEL_GEMS ? 999 :
9687      action_type == CA_SET_LEVEL_TIME ? 9999 :
9688      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9689      action_type == CA_SET_CE_VALUE ? 9999 :
9690      action_type == CA_SET_CE_SCORE ? 9999 :
9691      CA_ARG_MAX);
9692
9693   int action_arg_number_reset =
9694     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9695      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9696      action_type == CA_SET_LEVEL_TIME ? level.time :
9697      action_type == CA_SET_LEVEL_SCORE ? 0 :
9698      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9699      action_type == CA_SET_CE_SCORE ? 0 :
9700      0);
9701
9702   int action_arg_number =
9703     (action_arg <= CA_ARG_MAX ? action_arg :
9704      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9705      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9706      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9707      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9708      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9709      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9710      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9711      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9712      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9713      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9714      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9715      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9716      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9717      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9718      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9719      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9720      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9721      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9722      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9723      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9724      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9725      -1);
9726
9727   int action_arg_number_old =
9728     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9729      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9730      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9731      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9732      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9733      0);
9734
9735   int action_arg_number_new =
9736     getModifiedActionNumber(action_arg_number_old,
9737                             action_mode, action_arg_number,
9738                             action_arg_number_min, action_arg_number_max);
9739
9740   int trigger_player_bits =
9741     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9742      change->actual_trigger_player_bits : change->trigger_player);
9743
9744   int action_arg_player_bits =
9745     (action_arg >= CA_ARG_PLAYER_1 &&
9746      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9747      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9748      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9749      PLAYER_BITS_ANY);
9750
9751   /* ---------- execute action  -------------------------------------------- */
9752
9753   switch (action_type)
9754   {
9755     case CA_NO_ACTION:
9756     {
9757       return;
9758     }
9759
9760     /* ---------- level actions  ------------------------------------------- */
9761
9762     case CA_RESTART_LEVEL:
9763     {
9764       game.restart_level = TRUE;
9765
9766       break;
9767     }
9768
9769     case CA_SHOW_ENVELOPE:
9770     {
9771       int element = getSpecialActionElement(action_arg_element,
9772                                             action_arg_number, EL_ENVELOPE_1);
9773
9774       if (IS_ENVELOPE(element))
9775         local_player->show_envelope = element;
9776
9777       break;
9778     }
9779
9780     case CA_SET_LEVEL_TIME:
9781     {
9782       if (level.time > 0)       /* only modify limited time value */
9783       {
9784         TimeLeft = action_arg_number_new;
9785
9786         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9787
9788         DisplayGameControlValues();
9789
9790         if (!TimeLeft && setup.time_limit)
9791           for (i = 0; i < MAX_PLAYERS; i++)
9792             KillPlayer(&stored_player[i]);
9793       }
9794
9795       break;
9796     }
9797
9798     case CA_SET_LEVEL_SCORE:
9799     {
9800       local_player->score = action_arg_number_new;
9801
9802       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9803
9804       DisplayGameControlValues();
9805
9806       break;
9807     }
9808
9809     case CA_SET_LEVEL_GEMS:
9810     {
9811       local_player->gems_still_needed = action_arg_number_new;
9812
9813       game.snapshot.collected_item = TRUE;
9814
9815       game_panel_controls[GAME_PANEL_GEMS].value =
9816         local_player->gems_still_needed;
9817
9818       DisplayGameControlValues();
9819
9820       break;
9821     }
9822
9823     case CA_SET_LEVEL_WIND:
9824     {
9825       game.wind_direction = action_arg_direction;
9826
9827       break;
9828     }
9829
9830     case CA_SET_LEVEL_RANDOM_SEED:
9831     {
9832       /* ensure that setting a new random seed while playing is predictable */
9833       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9834
9835       break;
9836     }
9837
9838     /* ---------- player actions  ------------------------------------------ */
9839
9840     case CA_MOVE_PLAYER:
9841     {
9842       /* automatically move to the next field in specified direction */
9843       for (i = 0; i < MAX_PLAYERS; i++)
9844         if (trigger_player_bits & (1 << i))
9845           stored_player[i].programmed_action = action_arg_direction;
9846
9847       break;
9848     }
9849
9850     case CA_EXIT_PLAYER:
9851     {
9852       for (i = 0; i < MAX_PLAYERS; i++)
9853         if (action_arg_player_bits & (1 << i))
9854           PlayerWins(&stored_player[i]);
9855
9856       break;
9857     }
9858
9859     case CA_KILL_PLAYER:
9860     {
9861       for (i = 0; i < MAX_PLAYERS; i++)
9862         if (action_arg_player_bits & (1 << i))
9863           KillPlayer(&stored_player[i]);
9864
9865       break;
9866     }
9867
9868     case CA_SET_PLAYER_KEYS:
9869     {
9870       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9871       int element = getSpecialActionElement(action_arg_element,
9872                                             action_arg_number, EL_KEY_1);
9873
9874       if (IS_KEY(element))
9875       {
9876         for (i = 0; i < MAX_PLAYERS; i++)
9877         {
9878           if (trigger_player_bits & (1 << i))
9879           {
9880             stored_player[i].key[KEY_NR(element)] = key_state;
9881
9882             DrawGameDoorValues();
9883           }
9884         }
9885       }
9886
9887       break;
9888     }
9889
9890     case CA_SET_PLAYER_SPEED:
9891     {
9892       for (i = 0; i < MAX_PLAYERS; i++)
9893       {
9894         if (trigger_player_bits & (1 << i))
9895         {
9896           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9897
9898           if (action_arg == CA_ARG_SPEED_FASTER &&
9899               stored_player[i].cannot_move)
9900           {
9901             action_arg_number = STEPSIZE_VERY_SLOW;
9902           }
9903           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9904                    action_arg == CA_ARG_SPEED_FASTER)
9905           {
9906             action_arg_number = 2;
9907             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9908                            CA_MODE_MULTIPLY);
9909           }
9910           else if (action_arg == CA_ARG_NUMBER_RESET)
9911           {
9912             action_arg_number = level.initial_player_stepsize[i];
9913           }
9914
9915           move_stepsize =
9916             getModifiedActionNumber(move_stepsize,
9917                                     action_mode,
9918                                     action_arg_number,
9919                                     action_arg_number_min,
9920                                     action_arg_number_max);
9921
9922           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9923         }
9924       }
9925
9926       break;
9927     }
9928
9929     case CA_SET_PLAYER_SHIELD:
9930     {
9931       for (i = 0; i < MAX_PLAYERS; i++)
9932       {
9933         if (trigger_player_bits & (1 << i))
9934         {
9935           if (action_arg == CA_ARG_SHIELD_OFF)
9936           {
9937             stored_player[i].shield_normal_time_left = 0;
9938             stored_player[i].shield_deadly_time_left = 0;
9939           }
9940           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9941           {
9942             stored_player[i].shield_normal_time_left = 999999;
9943           }
9944           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9945           {
9946             stored_player[i].shield_normal_time_left = 999999;
9947             stored_player[i].shield_deadly_time_left = 999999;
9948           }
9949         }
9950       }
9951
9952       break;
9953     }
9954
9955     case CA_SET_PLAYER_GRAVITY:
9956     {
9957       for (i = 0; i < MAX_PLAYERS; i++)
9958       {
9959         if (trigger_player_bits & (1 << i))
9960         {
9961           stored_player[i].gravity =
9962             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9963              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9964              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9965              stored_player[i].gravity);
9966         }
9967       }
9968
9969       break;
9970     }
9971
9972     case CA_SET_PLAYER_ARTWORK:
9973     {
9974       for (i = 0; i < MAX_PLAYERS; i++)
9975       {
9976         if (trigger_player_bits & (1 << i))
9977         {
9978           int artwork_element = action_arg_element;
9979
9980           if (action_arg == CA_ARG_ELEMENT_RESET)
9981             artwork_element =
9982               (level.use_artwork_element[i] ? level.artwork_element[i] :
9983                stored_player[i].element_nr);
9984
9985           if (stored_player[i].artwork_element != artwork_element)
9986             stored_player[i].Frame = 0;
9987
9988           stored_player[i].artwork_element = artwork_element;
9989
9990           SetPlayerWaiting(&stored_player[i], FALSE);
9991
9992           /* set number of special actions for bored and sleeping animation */
9993           stored_player[i].num_special_action_bored =
9994             get_num_special_action(artwork_element,
9995                                    ACTION_BORING_1, ACTION_BORING_LAST);
9996           stored_player[i].num_special_action_sleeping =
9997             get_num_special_action(artwork_element,
9998                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9999         }
10000       }
10001
10002       break;
10003     }
10004
10005     case CA_SET_PLAYER_INVENTORY:
10006     {
10007       for (i = 0; i < MAX_PLAYERS; i++)
10008       {
10009         struct PlayerInfo *player = &stored_player[i];
10010         int j, k;
10011
10012         if (trigger_player_bits & (1 << i))
10013         {
10014           int inventory_element = action_arg_element;
10015
10016           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10017               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10018               action_arg == CA_ARG_ELEMENT_ACTION)
10019           {
10020             int element = inventory_element;
10021             int collect_count = element_info[element].collect_count_initial;
10022
10023             if (!IS_CUSTOM_ELEMENT(element))
10024               collect_count = 1;
10025
10026             if (collect_count == 0)
10027               player->inventory_infinite_element = element;
10028             else
10029               for (k = 0; k < collect_count; k++)
10030                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10031                   player->inventory_element[player->inventory_size++] =
10032                     element;
10033           }
10034           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10035                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10036                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10037           {
10038             if (player->inventory_infinite_element != EL_UNDEFINED &&
10039                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10040                                      action_arg_element_raw))
10041               player->inventory_infinite_element = EL_UNDEFINED;
10042
10043             for (k = 0, j = 0; j < player->inventory_size; j++)
10044             {
10045               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10046                                         action_arg_element_raw))
10047                 player->inventory_element[k++] = player->inventory_element[j];
10048             }
10049
10050             player->inventory_size = k;
10051           }
10052           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10053           {
10054             if (player->inventory_size > 0)
10055             {
10056               for (j = 0; j < player->inventory_size - 1; j++)
10057                 player->inventory_element[j] = player->inventory_element[j + 1];
10058
10059               player->inventory_size--;
10060             }
10061           }
10062           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10063           {
10064             if (player->inventory_size > 0)
10065               player->inventory_size--;
10066           }
10067           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10068           {
10069             player->inventory_infinite_element = EL_UNDEFINED;
10070             player->inventory_size = 0;
10071           }
10072           else if (action_arg == CA_ARG_INVENTORY_RESET)
10073           {
10074             player->inventory_infinite_element = EL_UNDEFINED;
10075             player->inventory_size = 0;
10076
10077             if (level.use_initial_inventory[i])
10078             {
10079               for (j = 0; j < level.initial_inventory_size[i]; j++)
10080               {
10081                 int element = level.initial_inventory_content[i][j];
10082                 int collect_count = element_info[element].collect_count_initial;
10083
10084                 if (!IS_CUSTOM_ELEMENT(element))
10085                   collect_count = 1;
10086
10087                 if (collect_count == 0)
10088                   player->inventory_infinite_element = element;
10089                 else
10090                   for (k = 0; k < collect_count; k++)
10091                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10092                       player->inventory_element[player->inventory_size++] =
10093                         element;
10094               }
10095             }
10096           }
10097         }
10098       }
10099
10100       break;
10101     }
10102
10103     /* ---------- CE actions  ---------------------------------------------- */
10104
10105     case CA_SET_CE_VALUE:
10106     {
10107       int last_ce_value = CustomValue[x][y];
10108
10109       CustomValue[x][y] = action_arg_number_new;
10110
10111       if (CustomValue[x][y] != last_ce_value)
10112       {
10113         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10114         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10115
10116         if (CustomValue[x][y] == 0)
10117         {
10118           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10119           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10120         }
10121       }
10122
10123       break;
10124     }
10125
10126     case CA_SET_CE_SCORE:
10127     {
10128       int last_ce_score = ei->collect_score;
10129
10130       ei->collect_score = action_arg_number_new;
10131
10132       if (ei->collect_score != last_ce_score)
10133       {
10134         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10135         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10136
10137         if (ei->collect_score == 0)
10138         {
10139           int xx, yy;
10140
10141           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10142           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10143
10144           /*
10145             This is a very special case that seems to be a mixture between
10146             CheckElementChange() and CheckTriggeredElementChange(): while
10147             the first one only affects single elements that are triggered
10148             directly, the second one affects multiple elements in the playfield
10149             that are triggered indirectly by another element. This is a third
10150             case: Changing the CE score always affects multiple identical CEs,
10151             so every affected CE must be checked, not only the single CE for
10152             which the CE score was changed in the first place (as every instance
10153             of that CE shares the same CE score, and therefore also can change)!
10154           */
10155           SCAN_PLAYFIELD(xx, yy)
10156           {
10157             if (Feld[xx][yy] == element)
10158               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10159                                  CE_SCORE_GETS_ZERO);
10160           }
10161         }
10162       }
10163
10164       break;
10165     }
10166
10167     case CA_SET_CE_ARTWORK:
10168     {
10169       int artwork_element = action_arg_element;
10170       boolean reset_frame = FALSE;
10171       int xx, yy;
10172
10173       if (action_arg == CA_ARG_ELEMENT_RESET)
10174         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10175                            element);
10176
10177       if (ei->gfx_element != artwork_element)
10178         reset_frame = TRUE;
10179
10180       ei->gfx_element = artwork_element;
10181
10182       SCAN_PLAYFIELD(xx, yy)
10183       {
10184         if (Feld[xx][yy] == element)
10185         {
10186           if (reset_frame)
10187           {
10188             ResetGfxAnimation(xx, yy);
10189             ResetRandomAnimationValue(xx, yy);
10190           }
10191
10192           TEST_DrawLevelField(xx, yy);
10193         }
10194       }
10195
10196       break;
10197     }
10198
10199     /* ---------- engine actions  ------------------------------------------ */
10200
10201     case CA_SET_ENGINE_SCAN_MODE:
10202     {
10203       InitPlayfieldScanMode(action_arg);
10204
10205       break;
10206     }
10207
10208     default:
10209       break;
10210   }
10211 }
10212
10213 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10214 {
10215   int old_element = Feld[x][y];
10216   int new_element = GetElementFromGroupElement(element);
10217   int previous_move_direction = MovDir[x][y];
10218   int last_ce_value = CustomValue[x][y];
10219   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10220   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10221   boolean add_player_onto_element = (new_element_is_player &&
10222                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10223                                      IS_WALKABLE(old_element));
10224
10225   if (!add_player_onto_element)
10226   {
10227     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10228       RemoveMovingField(x, y);
10229     else
10230       RemoveField(x, y);
10231
10232     Feld[x][y] = new_element;
10233
10234     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10235       MovDir[x][y] = previous_move_direction;
10236
10237     if (element_info[new_element].use_last_ce_value)
10238       CustomValue[x][y] = last_ce_value;
10239
10240     InitField_WithBug1(x, y, FALSE);
10241
10242     new_element = Feld[x][y];   /* element may have changed */
10243
10244     ResetGfxAnimation(x, y);
10245     ResetRandomAnimationValue(x, y);
10246
10247     TEST_DrawLevelField(x, y);
10248
10249     if (GFX_CRUMBLED(new_element))
10250       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10251   }
10252
10253   /* check if element under the player changes from accessible to unaccessible
10254      (needed for special case of dropping element which then changes) */
10255   /* (must be checked after creating new element for walkable group elements) */
10256   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10257       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10258   {
10259     Bang(x, y);
10260
10261     return;
10262   }
10263
10264   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10265   if (new_element_is_player)
10266     RelocatePlayer(x, y, new_element);
10267
10268   if (is_change)
10269     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10270
10271   TestIfBadThingTouchesPlayer(x, y);
10272   TestIfPlayerTouchesCustomElement(x, y);
10273   TestIfElementTouchesCustomElement(x, y);
10274 }
10275
10276 static void CreateField(int x, int y, int element)
10277 {
10278   CreateFieldExt(x, y, element, FALSE);
10279 }
10280
10281 static void CreateElementFromChange(int x, int y, int element)
10282 {
10283   element = GET_VALID_RUNTIME_ELEMENT(element);
10284
10285   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10286   {
10287     int old_element = Feld[x][y];
10288
10289     /* prevent changed element from moving in same engine frame
10290        unless both old and new element can either fall or move */
10291     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10292         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10293       Stop[x][y] = TRUE;
10294   }
10295
10296   CreateFieldExt(x, y, element, TRUE);
10297 }
10298
10299 static boolean ChangeElement(int x, int y, int element, int page)
10300 {
10301   struct ElementInfo *ei = &element_info[element];
10302   struct ElementChangeInfo *change = &ei->change_page[page];
10303   int ce_value = CustomValue[x][y];
10304   int ce_score = ei->collect_score;
10305   int target_element;
10306   int old_element = Feld[x][y];
10307
10308   /* always use default change event to prevent running into a loop */
10309   if (ChangeEvent[x][y] == -1)
10310     ChangeEvent[x][y] = CE_DELAY;
10311
10312   if (ChangeEvent[x][y] == CE_DELAY)
10313   {
10314     /* reset actual trigger element, trigger player and action element */
10315     change->actual_trigger_element = EL_EMPTY;
10316     change->actual_trigger_player = EL_EMPTY;
10317     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10318     change->actual_trigger_side = CH_SIDE_NONE;
10319     change->actual_trigger_ce_value = 0;
10320     change->actual_trigger_ce_score = 0;
10321   }
10322
10323   /* do not change elements more than a specified maximum number of changes */
10324   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10325     return FALSE;
10326
10327   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10328
10329   if (change->explode)
10330   {
10331     Bang(x, y);
10332
10333     return TRUE;
10334   }
10335
10336   if (change->use_target_content)
10337   {
10338     boolean complete_replace = TRUE;
10339     boolean can_replace[3][3];
10340     int xx, yy;
10341
10342     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10343     {
10344       boolean is_empty;
10345       boolean is_walkable;
10346       boolean is_diggable;
10347       boolean is_collectible;
10348       boolean is_removable;
10349       boolean is_destructible;
10350       int ex = x + xx - 1;
10351       int ey = y + yy - 1;
10352       int content_element = change->target_content.e[xx][yy];
10353       int e;
10354
10355       can_replace[xx][yy] = TRUE;
10356
10357       if (ex == x && ey == y)   /* do not check changing element itself */
10358         continue;
10359
10360       if (content_element == EL_EMPTY_SPACE)
10361       {
10362         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10363
10364         continue;
10365       }
10366
10367       if (!IN_LEV_FIELD(ex, ey))
10368       {
10369         can_replace[xx][yy] = FALSE;
10370         complete_replace = FALSE;
10371
10372         continue;
10373       }
10374
10375       e = Feld[ex][ey];
10376
10377       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10378         e = MovingOrBlocked2Element(ex, ey);
10379
10380       is_empty = (IS_FREE(ex, ey) ||
10381                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10382
10383       is_walkable     = (is_empty || IS_WALKABLE(e));
10384       is_diggable     = (is_empty || IS_DIGGABLE(e));
10385       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10386       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10387       is_removable    = (is_diggable || is_collectible);
10388
10389       can_replace[xx][yy] =
10390         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10391           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10392           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10393           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10394           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10395           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10396          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10397
10398       if (!can_replace[xx][yy])
10399         complete_replace = FALSE;
10400     }
10401
10402     if (!change->only_if_complete || complete_replace)
10403     {
10404       boolean something_has_changed = FALSE;
10405
10406       if (change->only_if_complete && change->use_random_replace &&
10407           RND(100) < change->random_percentage)
10408         return FALSE;
10409
10410       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10411       {
10412         int ex = x + xx - 1;
10413         int ey = y + yy - 1;
10414         int content_element;
10415
10416         if (can_replace[xx][yy] && (!change->use_random_replace ||
10417                                     RND(100) < change->random_percentage))
10418         {
10419           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10420             RemoveMovingField(ex, ey);
10421
10422           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10423
10424           content_element = change->target_content.e[xx][yy];
10425           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10426                                               ce_value, ce_score);
10427
10428           CreateElementFromChange(ex, ey, target_element);
10429
10430           something_has_changed = TRUE;
10431
10432           /* for symmetry reasons, freeze newly created border elements */
10433           if (ex != x || ey != y)
10434             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10435         }
10436       }
10437
10438       if (something_has_changed)
10439       {
10440         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10441         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10442       }
10443     }
10444   }
10445   else
10446   {
10447     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10448                                         ce_value, ce_score);
10449
10450     if (element == EL_DIAGONAL_GROWING ||
10451         element == EL_DIAGONAL_SHRINKING)
10452     {
10453       target_element = Store[x][y];
10454
10455       Store[x][y] = EL_EMPTY;
10456     }
10457
10458     CreateElementFromChange(x, y, target_element);
10459
10460     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10461     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10462   }
10463
10464   /* this uses direct change before indirect change */
10465   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10466
10467   return TRUE;
10468 }
10469
10470 static void HandleElementChange(int x, int y, int page)
10471 {
10472   int element = MovingOrBlocked2Element(x, y);
10473   struct ElementInfo *ei = &element_info[element];
10474   struct ElementChangeInfo *change = &ei->change_page[page];
10475   boolean handle_action_before_change = FALSE;
10476
10477 #ifdef DEBUG
10478   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10479       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10480   {
10481     printf("\n\n");
10482     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10483            x, y, element, element_info[element].token_name);
10484     printf("HandleElementChange(): This should never happen!\n");
10485     printf("\n\n");
10486   }
10487 #endif
10488
10489   /* this can happen with classic bombs on walkable, changing elements */
10490   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10491   {
10492     return;
10493   }
10494
10495   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10496   {
10497     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10498
10499     if (change->can_change)
10500     {
10501       /* !!! not clear why graphic animation should be reset at all here !!! */
10502       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10503       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10504
10505       /*
10506         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10507
10508         When using an animation frame delay of 1 (this only happens with
10509         "sp_zonk.moving.left/right" in the classic graphics), the default
10510         (non-moving) animation shows wrong animation frames (while the
10511         moving animation, like "sp_zonk.moving.left/right", is correct,
10512         so this graphical bug never shows up with the classic graphics).
10513         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10514         be drawn instead of the correct frames 0,1,2,3. This is caused by
10515         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10516         an element change: First when the change delay ("ChangeDelay[][]")
10517         counter has reached zero after decrementing, then a second time in
10518         the next frame (after "GfxFrame[][]" was already incremented) when
10519         "ChangeDelay[][]" is reset to the initial delay value again.
10520
10521         This causes frame 0 to be drawn twice, while the last frame won't
10522         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10523
10524         As some animations may already be cleverly designed around this bug
10525         (at least the "Snake Bite" snake tail animation does this), it cannot
10526         simply be fixed here without breaking such existing animations.
10527         Unfortunately, it cannot easily be detected if a graphics set was
10528         designed "before" or "after" the bug was fixed. As a workaround,
10529         a new graphics set option "game.graphics_engine_version" was added
10530         to be able to specify the game's major release version for which the
10531         graphics set was designed, which can then be used to decide if the
10532         bugfix should be used (version 4 and above) or not (version 3 or
10533         below, or if no version was specified at all, as with old sets).
10534
10535         (The wrong/fixed animation frames can be tested with the test level set
10536         "test_gfxframe" and level "000", which contains a specially prepared
10537         custom element at level position (x/y) == (11/9) which uses the zonk
10538         animation mentioned above. Using "game.graphics_engine_version: 4"
10539         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10540         This can also be seen from the debug output for this test element.)
10541       */
10542
10543       /* when a custom element is about to change (for example by change delay),
10544          do not reset graphic animation when the custom element is moving */
10545       if (game.graphics_engine_version < 4 &&
10546           !IS_MOVING(x, y))
10547       {
10548         ResetGfxAnimation(x, y);
10549         ResetRandomAnimationValue(x, y);
10550       }
10551
10552       if (change->pre_change_function)
10553         change->pre_change_function(x, y);
10554     }
10555   }
10556
10557   ChangeDelay[x][y]--;
10558
10559   if (ChangeDelay[x][y] != 0)           /* continue element change */
10560   {
10561     if (change->can_change)
10562     {
10563       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10564
10565       if (IS_ANIMATED(graphic))
10566         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10567
10568       if (change->change_function)
10569         change->change_function(x, y);
10570     }
10571   }
10572   else                                  /* finish element change */
10573   {
10574     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10575     {
10576       page = ChangePage[x][y];
10577       ChangePage[x][y] = -1;
10578
10579       change = &ei->change_page[page];
10580     }
10581
10582     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10583     {
10584       ChangeDelay[x][y] = 1;            /* try change after next move step */
10585       ChangePage[x][y] = page;          /* remember page to use for change */
10586
10587       return;
10588     }
10589
10590     /* special case: set new level random seed before changing element */
10591     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10592       handle_action_before_change = TRUE;
10593
10594     if (change->has_action && handle_action_before_change)
10595       ExecuteCustomElementAction(x, y, element, page);
10596
10597     if (change->can_change)
10598     {
10599       if (ChangeElement(x, y, element, page))
10600       {
10601         if (change->post_change_function)
10602           change->post_change_function(x, y);
10603       }
10604     }
10605
10606     if (change->has_action && !handle_action_before_change)
10607       ExecuteCustomElementAction(x, y, element, page);
10608   }
10609 }
10610
10611 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10612                                               int trigger_element,
10613                                               int trigger_event,
10614                                               int trigger_player,
10615                                               int trigger_side,
10616                                               int trigger_page)
10617 {
10618   boolean change_done_any = FALSE;
10619   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10620   int i;
10621
10622   if (!(trigger_events[trigger_element][trigger_event]))
10623     return FALSE;
10624
10625   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10626
10627   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10628   {
10629     int element = EL_CUSTOM_START + i;
10630     boolean change_done = FALSE;
10631     int p;
10632
10633     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10634         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10635       continue;
10636
10637     for (p = 0; p < element_info[element].num_change_pages; p++)
10638     {
10639       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10640
10641       if (change->can_change_or_has_action &&
10642           change->has_event[trigger_event] &&
10643           change->trigger_side & trigger_side &&
10644           change->trigger_player & trigger_player &&
10645           change->trigger_page & trigger_page_bits &&
10646           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10647       {
10648         change->actual_trigger_element = trigger_element;
10649         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10650         change->actual_trigger_player_bits = trigger_player;
10651         change->actual_trigger_side = trigger_side;
10652         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10653         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10654
10655         if ((change->can_change && !change_done) || change->has_action)
10656         {
10657           int x, y;
10658
10659           SCAN_PLAYFIELD(x, y)
10660           {
10661             if (Feld[x][y] == element)
10662             {
10663               if (change->can_change && !change_done)
10664               {
10665                 /* if element already changed in this frame, not only prevent
10666                    another element change (checked in ChangeElement()), but
10667                    also prevent additional element actions for this element */
10668
10669                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10670                     !level.use_action_after_change_bug)
10671                   continue;
10672
10673                 ChangeDelay[x][y] = 1;
10674                 ChangeEvent[x][y] = trigger_event;
10675
10676                 HandleElementChange(x, y, p);
10677               }
10678               else if (change->has_action)
10679               {
10680                 /* if element already changed in this frame, not only prevent
10681                    another element change (checked in ChangeElement()), but
10682                    also prevent additional element actions for this element */
10683
10684                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10685                     !level.use_action_after_change_bug)
10686                   continue;
10687
10688                 ExecuteCustomElementAction(x, y, element, p);
10689                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10690               }
10691             }
10692           }
10693
10694           if (change->can_change)
10695           {
10696             change_done = TRUE;
10697             change_done_any = TRUE;
10698           }
10699         }
10700       }
10701     }
10702   }
10703
10704   RECURSION_LOOP_DETECTION_END();
10705
10706   return change_done_any;
10707 }
10708
10709 static boolean CheckElementChangeExt(int x, int y,
10710                                      int element,
10711                                      int trigger_element,
10712                                      int trigger_event,
10713                                      int trigger_player,
10714                                      int trigger_side)
10715 {
10716   boolean change_done = FALSE;
10717   int p;
10718
10719   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10720       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10721     return FALSE;
10722
10723   if (Feld[x][y] == EL_BLOCKED)
10724   {
10725     Blocked2Moving(x, y, &x, &y);
10726     element = Feld[x][y];
10727   }
10728
10729   /* check if element has already changed or is about to change after moving */
10730   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10731        Feld[x][y] != element) ||
10732
10733       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10734        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10735         ChangePage[x][y] != -1)))
10736     return FALSE;
10737
10738   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10739
10740   for (p = 0; p < element_info[element].num_change_pages; p++)
10741   {
10742     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10743
10744     /* check trigger element for all events where the element that is checked
10745        for changing interacts with a directly adjacent element -- this is
10746        different to element changes that affect other elements to change on the
10747        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10748     boolean check_trigger_element =
10749       (trigger_event == CE_TOUCHING_X ||
10750        trigger_event == CE_HITTING_X ||
10751        trigger_event == CE_HIT_BY_X ||
10752        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10753
10754     if (change->can_change_or_has_action &&
10755         change->has_event[trigger_event] &&
10756         change->trigger_side & trigger_side &&
10757         change->trigger_player & trigger_player &&
10758         (!check_trigger_element ||
10759          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10760     {
10761       change->actual_trigger_element = trigger_element;
10762       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10763       change->actual_trigger_player_bits = trigger_player;
10764       change->actual_trigger_side = trigger_side;
10765       change->actual_trigger_ce_value = CustomValue[x][y];
10766       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10767
10768       /* special case: trigger element not at (x,y) position for some events */
10769       if (check_trigger_element)
10770       {
10771         static struct
10772         {
10773           int dx, dy;
10774         } move_xy[] =
10775           {
10776             {  0,  0 },
10777             { -1,  0 },
10778             { +1,  0 },
10779             {  0,  0 },
10780             {  0, -1 },
10781             {  0,  0 }, { 0, 0 }, { 0, 0 },
10782             {  0, +1 }
10783           };
10784
10785         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10786         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10787
10788         change->actual_trigger_ce_value = CustomValue[xx][yy];
10789         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10790       }
10791
10792       if (change->can_change && !change_done)
10793       {
10794         ChangeDelay[x][y] = 1;
10795         ChangeEvent[x][y] = trigger_event;
10796
10797         HandleElementChange(x, y, p);
10798
10799         change_done = TRUE;
10800       }
10801       else if (change->has_action)
10802       {
10803         ExecuteCustomElementAction(x, y, element, p);
10804         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10805       }
10806     }
10807   }
10808
10809   RECURSION_LOOP_DETECTION_END();
10810
10811   return change_done;
10812 }
10813
10814 static void PlayPlayerSound(struct PlayerInfo *player)
10815 {
10816   int jx = player->jx, jy = player->jy;
10817   int sound_element = player->artwork_element;
10818   int last_action = player->last_action_waiting;
10819   int action = player->action_waiting;
10820
10821   if (player->is_waiting)
10822   {
10823     if (action != last_action)
10824       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10825     else
10826       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10827   }
10828   else
10829   {
10830     if (action != last_action)
10831       StopSound(element_info[sound_element].sound[last_action]);
10832
10833     if (last_action == ACTION_SLEEPING)
10834       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10835   }
10836 }
10837
10838 static void PlayAllPlayersSound()
10839 {
10840   int i;
10841
10842   for (i = 0; i < MAX_PLAYERS; i++)
10843     if (stored_player[i].active)
10844       PlayPlayerSound(&stored_player[i]);
10845 }
10846
10847 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10848 {
10849   boolean last_waiting = player->is_waiting;
10850   int move_dir = player->MovDir;
10851
10852   player->dir_waiting = move_dir;
10853   player->last_action_waiting = player->action_waiting;
10854
10855   if (is_waiting)
10856   {
10857     if (!last_waiting)          /* not waiting -> waiting */
10858     {
10859       player->is_waiting = TRUE;
10860
10861       player->frame_counter_bored =
10862         FrameCounter +
10863         game.player_boring_delay_fixed +
10864         GetSimpleRandom(game.player_boring_delay_random);
10865       player->frame_counter_sleeping =
10866         FrameCounter +
10867         game.player_sleeping_delay_fixed +
10868         GetSimpleRandom(game.player_sleeping_delay_random);
10869
10870       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10871     }
10872
10873     if (game.player_sleeping_delay_fixed +
10874         game.player_sleeping_delay_random > 0 &&
10875         player->anim_delay_counter == 0 &&
10876         player->post_delay_counter == 0 &&
10877         FrameCounter >= player->frame_counter_sleeping)
10878       player->is_sleeping = TRUE;
10879     else if (game.player_boring_delay_fixed +
10880              game.player_boring_delay_random > 0 &&
10881              FrameCounter >= player->frame_counter_bored)
10882       player->is_bored = TRUE;
10883
10884     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10885                               player->is_bored ? ACTION_BORING :
10886                               ACTION_WAITING);
10887
10888     if (player->is_sleeping && player->use_murphy)
10889     {
10890       /* special case for sleeping Murphy when leaning against non-free tile */
10891
10892       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10893           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10894            !IS_MOVING(player->jx - 1, player->jy)))
10895         move_dir = MV_LEFT;
10896       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10897                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10898                 !IS_MOVING(player->jx + 1, player->jy)))
10899         move_dir = MV_RIGHT;
10900       else
10901         player->is_sleeping = FALSE;
10902
10903       player->dir_waiting = move_dir;
10904     }
10905
10906     if (player->is_sleeping)
10907     {
10908       if (player->num_special_action_sleeping > 0)
10909       {
10910         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10911         {
10912           int last_special_action = player->special_action_sleeping;
10913           int num_special_action = player->num_special_action_sleeping;
10914           int special_action =
10915             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10916              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10917              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10918              last_special_action + 1 : ACTION_SLEEPING);
10919           int special_graphic =
10920             el_act_dir2img(player->artwork_element, special_action, move_dir);
10921
10922           player->anim_delay_counter =
10923             graphic_info[special_graphic].anim_delay_fixed +
10924             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10925           player->post_delay_counter =
10926             graphic_info[special_graphic].post_delay_fixed +
10927             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10928
10929           player->special_action_sleeping = special_action;
10930         }
10931
10932         if (player->anim_delay_counter > 0)
10933         {
10934           player->action_waiting = player->special_action_sleeping;
10935           player->anim_delay_counter--;
10936         }
10937         else if (player->post_delay_counter > 0)
10938         {
10939           player->post_delay_counter--;
10940         }
10941       }
10942     }
10943     else if (player->is_bored)
10944     {
10945       if (player->num_special_action_bored > 0)
10946       {
10947         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10948         {
10949           int special_action =
10950             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10951           int special_graphic =
10952             el_act_dir2img(player->artwork_element, special_action, move_dir);
10953
10954           player->anim_delay_counter =
10955             graphic_info[special_graphic].anim_delay_fixed +
10956             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10957           player->post_delay_counter =
10958             graphic_info[special_graphic].post_delay_fixed +
10959             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10960
10961           player->special_action_bored = special_action;
10962         }
10963
10964         if (player->anim_delay_counter > 0)
10965         {
10966           player->action_waiting = player->special_action_bored;
10967           player->anim_delay_counter--;
10968         }
10969         else if (player->post_delay_counter > 0)
10970         {
10971           player->post_delay_counter--;
10972         }
10973       }
10974     }
10975   }
10976   else if (last_waiting)        /* waiting -> not waiting */
10977   {
10978     player->is_waiting = FALSE;
10979     player->is_bored = FALSE;
10980     player->is_sleeping = FALSE;
10981
10982     player->frame_counter_bored = -1;
10983     player->frame_counter_sleeping = -1;
10984
10985     player->anim_delay_counter = 0;
10986     player->post_delay_counter = 0;
10987
10988     player->dir_waiting = player->MovDir;
10989     player->action_waiting = ACTION_DEFAULT;
10990
10991     player->special_action_bored = ACTION_DEFAULT;
10992     player->special_action_sleeping = ACTION_DEFAULT;
10993   }
10994 }
10995
10996 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10997 {
10998   if ((!player->is_moving  && player->was_moving) ||
10999       (player->MovPos == 0 && player->was_moving) ||
11000       (player->is_snapping && !player->was_snapping) ||
11001       (player->is_dropping && !player->was_dropping))
11002   {
11003     if (!CheckSaveEngineSnapshotToList())
11004       return;
11005
11006     player->was_moving = FALSE;
11007     player->was_snapping = TRUE;
11008     player->was_dropping = TRUE;
11009   }
11010   else
11011   {
11012     if (player->is_moving)
11013       player->was_moving = TRUE;
11014
11015     if (!player->is_snapping)
11016       player->was_snapping = FALSE;
11017
11018     if (!player->is_dropping)
11019       player->was_dropping = FALSE;
11020   }
11021 }
11022
11023 static void CheckSingleStepMode(struct PlayerInfo *player)
11024 {
11025   if (tape.single_step && tape.recording && !tape.pausing)
11026   {
11027     /* as it is called "single step mode", just return to pause mode when the
11028        player stopped moving after one tile (or never starts moving at all) */
11029     if (!player->is_moving &&
11030         !player->is_pushing &&
11031         !player->is_dropping_pressed)
11032     {
11033       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11034       SnapField(player, 0, 0);                  /* stop snapping */
11035     }
11036   }
11037
11038   CheckSaveEngineSnapshot(player);
11039 }
11040
11041 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11042 {
11043   int left      = player_action & JOY_LEFT;
11044   int right     = player_action & JOY_RIGHT;
11045   int up        = player_action & JOY_UP;
11046   int down      = player_action & JOY_DOWN;
11047   int button1   = player_action & JOY_BUTTON_1;
11048   int button2   = player_action & JOY_BUTTON_2;
11049   int dx        = (left ? -1 : right ? 1 : 0);
11050   int dy        = (up   ? -1 : down  ? 1 : 0);
11051
11052   if (!player->active || tape.pausing)
11053     return 0;
11054
11055   if (player_action)
11056   {
11057     if (button1)
11058       SnapField(player, dx, dy);
11059     else
11060     {
11061       if (button2)
11062         DropElement(player);
11063
11064       MovePlayer(player, dx, dy);
11065     }
11066
11067     CheckSingleStepMode(player);
11068
11069     SetPlayerWaiting(player, FALSE);
11070
11071     return player_action;
11072   }
11073   else
11074   {
11075     /* no actions for this player (no input at player's configured device) */
11076
11077     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11078     SnapField(player, 0, 0);
11079     CheckGravityMovementWhenNotMoving(player);
11080
11081     if (player->MovPos == 0)
11082       SetPlayerWaiting(player, TRUE);
11083
11084     if (player->MovPos == 0)    /* needed for tape.playing */
11085       player->is_moving = FALSE;
11086
11087     player->is_dropping = FALSE;
11088     player->is_dropping_pressed = FALSE;
11089     player->drop_pressed_delay = 0;
11090
11091     CheckSingleStepMode(player);
11092
11093     return 0;
11094   }
11095 }
11096
11097 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11098                                          byte *tape_action)
11099 {
11100   if (!tape.use_mouse)
11101     return;
11102
11103   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11104   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11105   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11106 }
11107
11108 static void SetTapeActionFromMouseAction(byte *tape_action,
11109                                          struct MouseActionInfo *mouse_action)
11110 {
11111   if (!tape.use_mouse)
11112     return;
11113
11114   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11115   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11116   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11117 }
11118
11119 static void CheckLevelTime()
11120 {
11121   int i;
11122
11123   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11124   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11125   {
11126     if (level.native_em_level->lev->home == 0)  /* all players at home */
11127     {
11128       PlayerWins(local_player);
11129
11130       AllPlayersGone = TRUE;
11131
11132       level.native_em_level->lev->home = -1;
11133     }
11134
11135     if (level.native_em_level->ply[0]->alive == 0 &&
11136         level.native_em_level->ply[1]->alive == 0 &&
11137         level.native_em_level->ply[2]->alive == 0 &&
11138         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11139       AllPlayersGone = TRUE;
11140   }
11141   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11142   {
11143     if (game_sp.LevelSolved &&
11144         !game_sp.GameOver)                              /* game won */
11145     {
11146       PlayerWins(local_player);
11147
11148       game_sp.GameOver = TRUE;
11149
11150       AllPlayersGone = TRUE;
11151     }
11152
11153     if (game_sp.GameOver)                               /* game lost */
11154       AllPlayersGone = TRUE;
11155   }
11156   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11157   {
11158     if (game_mm.level_solved &&
11159         !game_mm.game_over)                             /* game won */
11160     {
11161       PlayerWins(local_player);
11162
11163       game_mm.game_over = TRUE;
11164
11165       AllPlayersGone = TRUE;
11166     }
11167
11168     if (game_mm.game_over)                              /* game lost */
11169       AllPlayersGone = TRUE;
11170   }
11171
11172   if (TimeFrames >= FRAMES_PER_SECOND)
11173   {
11174     TimeFrames = 0;
11175     TapeTime++;
11176
11177     for (i = 0; i < MAX_PLAYERS; i++)
11178     {
11179       struct PlayerInfo *player = &stored_player[i];
11180
11181       if (SHIELD_ON(player))
11182       {
11183         player->shield_normal_time_left--;
11184
11185         if (player->shield_deadly_time_left > 0)
11186           player->shield_deadly_time_left--;
11187       }
11188     }
11189
11190     if (!local_player->LevelSolved && !level.use_step_counter)
11191     {
11192       TimePlayed++;
11193
11194       if (TimeLeft > 0)
11195       {
11196         TimeLeft--;
11197
11198         if (TimeLeft <= 10 && setup.time_limit)
11199           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11200
11201         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11202            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11203
11204         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11205
11206         if (!TimeLeft && setup.time_limit)
11207         {
11208           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11209             level.native_em_level->lev->killed_out_of_time = TRUE;
11210           else
11211             for (i = 0; i < MAX_PLAYERS; i++)
11212               KillPlayer(&stored_player[i]);
11213         }
11214       }
11215       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11216       {
11217         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11218       }
11219
11220       level.native_em_level->lev->time =
11221         (game.no_time_limit ? TimePlayed : TimeLeft);
11222     }
11223
11224     if (tape.recording || tape.playing)
11225       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11226   }
11227
11228   if (tape.recording || tape.playing)
11229     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11230
11231   UpdateAndDisplayGameControlValues();
11232 }
11233
11234 void AdvanceFrameAndPlayerCounters(int player_nr)
11235 {
11236   int i;
11237
11238   /* advance frame counters (global frame counter and time frame counter) */
11239   FrameCounter++;
11240   TimeFrames++;
11241
11242   /* advance player counters (counters for move delay, move animation etc.) */
11243   for (i = 0; i < MAX_PLAYERS; i++)
11244   {
11245     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11246     int move_delay_value = stored_player[i].move_delay_value;
11247     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11248
11249     if (!advance_player_counters)       /* not all players may be affected */
11250       continue;
11251
11252     if (move_frames == 0)       /* less than one move per game frame */
11253     {
11254       int stepsize = TILEX / move_delay_value;
11255       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11256       int count = (stored_player[i].is_moving ?
11257                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11258
11259       if (count % delay == 0)
11260         move_frames = 1;
11261     }
11262
11263     stored_player[i].Frame += move_frames;
11264
11265     if (stored_player[i].MovPos != 0)
11266       stored_player[i].StepFrame += move_frames;
11267
11268     if (stored_player[i].move_delay > 0)
11269       stored_player[i].move_delay--;
11270
11271     /* due to bugs in previous versions, counter must count up, not down */
11272     if (stored_player[i].push_delay != -1)
11273       stored_player[i].push_delay++;
11274
11275     if (stored_player[i].drop_delay > 0)
11276       stored_player[i].drop_delay--;
11277
11278     if (stored_player[i].is_dropping_pressed)
11279       stored_player[i].drop_pressed_delay++;
11280   }
11281 }
11282
11283 void StartGameActions(boolean init_network_game, boolean record_tape,
11284                       int random_seed)
11285 {
11286   unsigned int new_random_seed = InitRND(random_seed);
11287
11288   if (record_tape)
11289     TapeStartRecording(new_random_seed);
11290
11291   if (init_network_game)
11292   {
11293     SendToServer_StartPlaying();
11294
11295     return;
11296   }
11297
11298   InitGame();
11299 }
11300
11301 void GameActionsExt()
11302 {
11303 #if 0
11304   static unsigned int game_frame_delay = 0;
11305 #endif
11306   unsigned int game_frame_delay_value;
11307   byte *recorded_player_action;
11308   byte summarized_player_action = 0;
11309   byte tape_action[MAX_PLAYERS];
11310   int i;
11311
11312   /* detect endless loops, caused by custom element programming */
11313   if (recursion_loop_detected && recursion_loop_depth == 0)
11314   {
11315     char *message = getStringCat3("Internal Error! Element ",
11316                                   EL_NAME(recursion_loop_element),
11317                                   " caused endless loop! Quit the game?");
11318
11319     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11320           EL_NAME(recursion_loop_element));
11321
11322     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11323
11324     recursion_loop_detected = FALSE;    /* if game should be continued */
11325
11326     free(message);
11327
11328     return;
11329   }
11330
11331   if (game.restart_level)
11332     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11333
11334   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11335   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11336   {
11337     if (level.native_em_level->lev->home == 0)  /* all players at home */
11338     {
11339       PlayerWins(local_player);
11340
11341       AllPlayersGone = TRUE;
11342
11343       level.native_em_level->lev->home = -1;
11344     }
11345
11346     if (level.native_em_level->ply[0]->alive == 0 &&
11347         level.native_em_level->ply[1]->alive == 0 &&
11348         level.native_em_level->ply[2]->alive == 0 &&
11349         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11350       AllPlayersGone = TRUE;
11351   }
11352   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11353   {
11354     if (game_sp.LevelSolved &&
11355         !game_sp.GameOver)                              /* game won */
11356     {
11357       PlayerWins(local_player);
11358
11359       game_sp.GameOver = TRUE;
11360
11361       AllPlayersGone = TRUE;
11362     }
11363
11364     if (game_sp.GameOver)                               /* game lost */
11365       AllPlayersGone = TRUE;
11366   }
11367   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11368   {
11369     if (game_mm.level_solved &&
11370         !game_mm.game_over)                             /* game won */
11371     {
11372       PlayerWins(local_player);
11373
11374       game_mm.game_over = TRUE;
11375
11376       AllPlayersGone = TRUE;
11377     }
11378
11379     if (game_mm.game_over)                              /* game lost */
11380       AllPlayersGone = TRUE;
11381   }
11382
11383   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11384     GameWon();
11385
11386   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11387     TapeStop();
11388
11389   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11390     return;
11391
11392   game_frame_delay_value =
11393     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11394
11395   if (tape.playing && tape.warp_forward && !tape.pausing)
11396     game_frame_delay_value = 0;
11397
11398   SetVideoFrameDelay(game_frame_delay_value);
11399
11400 #if 0
11401 #if 0
11402   /* ---------- main game synchronization point ---------- */
11403
11404   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11405
11406   printf("::: skip == %d\n", skip);
11407
11408 #else
11409   /* ---------- main game synchronization point ---------- */
11410
11411   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11412 #endif
11413 #endif
11414
11415   if (network_playing && !network_player_action_received)
11416   {
11417     /* try to get network player actions in time */
11418
11419     /* last chance to get network player actions without main loop delay */
11420     HandleNetworking();
11421
11422     /* game was quit by network peer */
11423     if (game_status != GAME_MODE_PLAYING)
11424       return;
11425
11426     if (!network_player_action_received)
11427       return;           /* failed to get network player actions in time */
11428
11429     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11430   }
11431
11432   if (tape.pausing)
11433     return;
11434
11435   /* at this point we know that we really continue executing the game */
11436
11437   network_player_action_received = FALSE;
11438
11439   /* when playing tape, read previously recorded player input from tape data */
11440   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11441
11442   local_player->effective_mouse_action = local_player->mouse_action;
11443
11444   if (recorded_player_action != NULL)
11445     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11446                                  recorded_player_action);
11447
11448   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11449   if (tape.pausing)
11450     return;
11451
11452   if (tape.set_centered_player)
11453   {
11454     game.centered_player_nr_next = tape.centered_player_nr_next;
11455     game.set_centered_player = TRUE;
11456   }
11457
11458   for (i = 0; i < MAX_PLAYERS; i++)
11459   {
11460     summarized_player_action |= stored_player[i].action;
11461
11462     if (!network_playing && (game.team_mode || tape.playing))
11463       stored_player[i].effective_action = stored_player[i].action;
11464   }
11465
11466   if (network_playing)
11467     SendToServer_MovePlayer(summarized_player_action);
11468
11469   // summarize all actions at local players mapped input device position
11470   // (this allows using different input devices in single player mode)
11471   if (!network.enabled && !game.team_mode)
11472     stored_player[map_player_action[local_player->index_nr]].effective_action =
11473       summarized_player_action;
11474
11475   if (tape.recording &&
11476       setup.team_mode &&
11477       setup.input_on_focus &&
11478       game.centered_player_nr != -1)
11479   {
11480     for (i = 0; i < MAX_PLAYERS; i++)
11481       stored_player[i].effective_action =
11482         (i == game.centered_player_nr ? summarized_player_action : 0);
11483   }
11484
11485   if (recorded_player_action != NULL)
11486     for (i = 0; i < MAX_PLAYERS; i++)
11487       stored_player[i].effective_action = recorded_player_action[i];
11488
11489   for (i = 0; i < MAX_PLAYERS; i++)
11490   {
11491     tape_action[i] = stored_player[i].effective_action;
11492
11493     /* (this may happen in the RND game engine if a player was not present on
11494        the playfield on level start, but appeared later from a custom element */
11495     if (setup.team_mode &&
11496         tape.recording &&
11497         tape_action[i] &&
11498         !tape.player_participates[i])
11499       tape.player_participates[i] = TRUE;
11500   }
11501
11502   SetTapeActionFromMouseAction(tape_action,
11503                                &local_player->effective_mouse_action);
11504
11505   /* only record actions from input devices, but not programmed actions */
11506   if (tape.recording)
11507     TapeRecordAction(tape_action);
11508
11509 #if USE_NEW_PLAYER_ASSIGNMENTS
11510   // !!! also map player actions in single player mode !!!
11511   // if (game.team_mode)
11512   if (1)
11513   {
11514     byte mapped_action[MAX_PLAYERS];
11515
11516 #if DEBUG_PLAYER_ACTIONS
11517     printf(":::");
11518     for (i = 0; i < MAX_PLAYERS; i++)
11519       printf(" %d, ", stored_player[i].effective_action);
11520 #endif
11521
11522     for (i = 0; i < MAX_PLAYERS; i++)
11523       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11524
11525     for (i = 0; i < MAX_PLAYERS; i++)
11526       stored_player[i].effective_action = mapped_action[i];
11527
11528 #if DEBUG_PLAYER_ACTIONS
11529     printf(" =>");
11530     for (i = 0; i < MAX_PLAYERS; i++)
11531       printf(" %d, ", stored_player[i].effective_action);
11532     printf("\n");
11533 #endif
11534   }
11535 #if DEBUG_PLAYER_ACTIONS
11536   else
11537   {
11538     printf(":::");
11539     for (i = 0; i < MAX_PLAYERS; i++)
11540       printf(" %d, ", stored_player[i].effective_action);
11541     printf("\n");
11542   }
11543 #endif
11544 #endif
11545
11546   for (i = 0; i < MAX_PLAYERS; i++)
11547   {
11548     // allow engine snapshot in case of changed movement attempt
11549     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11550         (stored_player[i].effective_action & KEY_MOTION))
11551       game.snapshot.changed_action = TRUE;
11552
11553     // allow engine snapshot in case of snapping/dropping attempt
11554     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11555         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11556       game.snapshot.changed_action = TRUE;
11557
11558     game.snapshot.last_action[i] = stored_player[i].effective_action;
11559   }
11560
11561   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11562   {
11563     GameActions_EM_Main();
11564   }
11565   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11566   {
11567     GameActions_SP_Main();
11568   }
11569   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11570   {
11571     GameActions_MM_Main();
11572   }
11573   else
11574   {
11575     GameActions_RND_Main();
11576   }
11577
11578   BlitScreenToBitmap(backbuffer);
11579
11580   CheckLevelTime();
11581
11582   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11583
11584   if (global.show_frames_per_second)
11585   {
11586     static unsigned int fps_counter = 0;
11587     static int fps_frames = 0;
11588     unsigned int fps_delay_ms = Counter() - fps_counter;
11589
11590     fps_frames++;
11591
11592     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11593     {
11594       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11595
11596       fps_frames = 0;
11597       fps_counter = Counter();
11598
11599       /* always draw FPS to screen after FPS value was updated */
11600       redraw_mask |= REDRAW_FPS;
11601     }
11602
11603     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11604     if (GetDrawDeactivationMask() == REDRAW_NONE)
11605       redraw_mask |= REDRAW_FPS;
11606   }
11607 }
11608
11609 static void GameActions_CheckSaveEngineSnapshot()
11610 {
11611   if (!game.snapshot.save_snapshot)
11612     return;
11613
11614   // clear flag for saving snapshot _before_ saving snapshot
11615   game.snapshot.save_snapshot = FALSE;
11616
11617   SaveEngineSnapshotToList();
11618 }
11619
11620 void GameActions()
11621 {
11622   GameActionsExt();
11623
11624   GameActions_CheckSaveEngineSnapshot();
11625 }
11626
11627 void GameActions_EM_Main()
11628 {
11629   byte effective_action[MAX_PLAYERS];
11630   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11631   int i;
11632
11633   for (i = 0; i < MAX_PLAYERS; i++)
11634     effective_action[i] = stored_player[i].effective_action;
11635
11636   GameActions_EM(effective_action, warp_mode);
11637 }
11638
11639 void GameActions_SP_Main()
11640 {
11641   byte effective_action[MAX_PLAYERS];
11642   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11643   int i;
11644
11645   for (i = 0; i < MAX_PLAYERS; i++)
11646     effective_action[i] = stored_player[i].effective_action;
11647
11648   GameActions_SP(effective_action, warp_mode);
11649
11650   for (i = 0; i < MAX_PLAYERS; i++)
11651   {
11652     if (stored_player[i].force_dropping)
11653       stored_player[i].action |= KEY_BUTTON_DROP;
11654
11655     stored_player[i].force_dropping = FALSE;
11656   }
11657 }
11658
11659 void GameActions_MM_Main()
11660 {
11661   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11662
11663   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11664 }
11665
11666 void GameActions_RND_Main()
11667 {
11668   GameActions_RND();
11669 }
11670
11671 void GameActions_RND()
11672 {
11673   int magic_wall_x = 0, magic_wall_y = 0;
11674   int i, x, y, element, graphic, last_gfx_frame;
11675
11676   InitPlayfieldScanModeVars();
11677
11678   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11679   {
11680     SCAN_PLAYFIELD(x, y)
11681     {
11682       ChangeCount[x][y] = 0;
11683       ChangeEvent[x][y] = -1;
11684     }
11685   }
11686
11687   if (game.set_centered_player)
11688   {
11689     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11690
11691     /* switching to "all players" only possible if all players fit to screen */
11692     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11693     {
11694       game.centered_player_nr_next = game.centered_player_nr;
11695       game.set_centered_player = FALSE;
11696     }
11697
11698     /* do not switch focus to non-existing (or non-active) player */
11699     if (game.centered_player_nr_next >= 0 &&
11700         !stored_player[game.centered_player_nr_next].active)
11701     {
11702       game.centered_player_nr_next = game.centered_player_nr;
11703       game.set_centered_player = FALSE;
11704     }
11705   }
11706
11707   if (game.set_centered_player &&
11708       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11709   {
11710     int sx, sy;
11711
11712     if (game.centered_player_nr_next == -1)
11713     {
11714       setScreenCenteredToAllPlayers(&sx, &sy);
11715     }
11716     else
11717     {
11718       sx = stored_player[game.centered_player_nr_next].jx;
11719       sy = stored_player[game.centered_player_nr_next].jy;
11720     }
11721
11722     game.centered_player_nr = game.centered_player_nr_next;
11723     game.set_centered_player = FALSE;
11724
11725     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11726     DrawGameDoorValues();
11727   }
11728
11729   for (i = 0; i < MAX_PLAYERS; i++)
11730   {
11731     int actual_player_action = stored_player[i].effective_action;
11732
11733 #if 1
11734     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11735        - rnd_equinox_tetrachloride 048
11736        - rnd_equinox_tetrachloride_ii 096
11737        - rnd_emanuel_schmieg 002
11738        - doctor_sloan_ww 001, 020
11739     */
11740     if (stored_player[i].MovPos == 0)
11741       CheckGravityMovement(&stored_player[i]);
11742 #endif
11743
11744     /* overwrite programmed action with tape action */
11745     if (stored_player[i].programmed_action)
11746       actual_player_action = stored_player[i].programmed_action;
11747
11748     PlayerActions(&stored_player[i], actual_player_action);
11749
11750     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11751   }
11752
11753   ScrollScreen(NULL, SCROLL_GO_ON);
11754
11755   /* for backwards compatibility, the following code emulates a fixed bug that
11756      occured when pushing elements (causing elements that just made their last
11757      pushing step to already (if possible) make their first falling step in the
11758      same game frame, which is bad); this code is also needed to use the famous
11759      "spring push bug" which is used in older levels and might be wanted to be
11760      used also in newer levels, but in this case the buggy pushing code is only
11761      affecting the "spring" element and no other elements */
11762
11763   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11764   {
11765     for (i = 0; i < MAX_PLAYERS; i++)
11766     {
11767       struct PlayerInfo *player = &stored_player[i];
11768       int x = player->jx;
11769       int y = player->jy;
11770
11771       if (player->active && player->is_pushing && player->is_moving &&
11772           IS_MOVING(x, y) &&
11773           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11774            Feld[x][y] == EL_SPRING))
11775       {
11776         ContinueMoving(x, y);
11777
11778         /* continue moving after pushing (this is actually a bug) */
11779         if (!IS_MOVING(x, y))
11780           Stop[x][y] = FALSE;
11781       }
11782     }
11783   }
11784
11785   SCAN_PLAYFIELD(x, y)
11786   {
11787     ChangeCount[x][y] = 0;
11788     ChangeEvent[x][y] = -1;
11789
11790     /* this must be handled before main playfield loop */
11791     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11792     {
11793       MovDelay[x][y]--;
11794       if (MovDelay[x][y] <= 0)
11795         RemoveField(x, y);
11796     }
11797
11798     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11799     {
11800       MovDelay[x][y]--;
11801       if (MovDelay[x][y] <= 0)
11802       {
11803         RemoveField(x, y);
11804         TEST_DrawLevelField(x, y);
11805
11806         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11807       }
11808     }
11809
11810 #if DEBUG
11811     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11812     {
11813       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11814       printf("GameActions(): This should never happen!\n");
11815
11816       ChangePage[x][y] = -1;
11817     }
11818 #endif
11819
11820     Stop[x][y] = FALSE;
11821     if (WasJustMoving[x][y] > 0)
11822       WasJustMoving[x][y]--;
11823     if (WasJustFalling[x][y] > 0)
11824       WasJustFalling[x][y]--;
11825     if (CheckCollision[x][y] > 0)
11826       CheckCollision[x][y]--;
11827     if (CheckImpact[x][y] > 0)
11828       CheckImpact[x][y]--;
11829
11830     GfxFrame[x][y]++;
11831
11832     /* reset finished pushing action (not done in ContinueMoving() to allow
11833        continuous pushing animation for elements with zero push delay) */
11834     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11835     {
11836       ResetGfxAnimation(x, y);
11837       TEST_DrawLevelField(x, y);
11838     }
11839
11840 #if DEBUG
11841     if (IS_BLOCKED(x, y))
11842     {
11843       int oldx, oldy;
11844
11845       Blocked2Moving(x, y, &oldx, &oldy);
11846       if (!IS_MOVING(oldx, oldy))
11847       {
11848         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11849         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11850         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11851         printf("GameActions(): This should never happen!\n");
11852       }
11853     }
11854 #endif
11855   }
11856
11857   SCAN_PLAYFIELD(x, y)
11858   {
11859     element = Feld[x][y];
11860     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11861     last_gfx_frame = GfxFrame[x][y];
11862
11863     ResetGfxFrame(x, y);
11864
11865     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11866       DrawLevelGraphicAnimation(x, y, graphic);
11867
11868     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11869         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11870       ResetRandomAnimationValue(x, y);
11871
11872     SetRandomAnimationValue(x, y);
11873
11874     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11875
11876     if (IS_INACTIVE(element))
11877     {
11878       if (IS_ANIMATED(graphic))
11879         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11880
11881       continue;
11882     }
11883
11884     /* this may take place after moving, so 'element' may have changed */
11885     if (IS_CHANGING(x, y) &&
11886         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11887     {
11888       int page = element_info[element].event_page_nr[CE_DELAY];
11889
11890       HandleElementChange(x, y, page);
11891
11892       element = Feld[x][y];
11893       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11894     }
11895
11896     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11897     {
11898       StartMoving(x, y);
11899
11900       element = Feld[x][y];
11901       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11902
11903       if (IS_ANIMATED(graphic) &&
11904           !IS_MOVING(x, y) &&
11905           !Stop[x][y])
11906         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11907
11908       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11909         TEST_DrawTwinkleOnField(x, y);
11910     }
11911     else if (element == EL_ACID)
11912     {
11913       if (!Stop[x][y])
11914         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11915     }
11916     else if ((element == EL_EXIT_OPEN ||
11917               element == EL_EM_EXIT_OPEN ||
11918               element == EL_SP_EXIT_OPEN ||
11919               element == EL_STEEL_EXIT_OPEN ||
11920               element == EL_EM_STEEL_EXIT_OPEN ||
11921               element == EL_SP_TERMINAL ||
11922               element == EL_SP_TERMINAL_ACTIVE ||
11923               element == EL_EXTRA_TIME ||
11924               element == EL_SHIELD_NORMAL ||
11925               element == EL_SHIELD_DEADLY) &&
11926              IS_ANIMATED(graphic))
11927       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11928     else if (IS_MOVING(x, y))
11929       ContinueMoving(x, y);
11930     else if (IS_ACTIVE_BOMB(element))
11931       CheckDynamite(x, y);
11932     else if (element == EL_AMOEBA_GROWING)
11933       AmoebeWaechst(x, y);
11934     else if (element == EL_AMOEBA_SHRINKING)
11935       AmoebaDisappearing(x, y);
11936
11937 #if !USE_NEW_AMOEBA_CODE
11938     else if (IS_AMOEBALIVE(element))
11939       AmoebeAbleger(x, y);
11940 #endif
11941
11942     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11943       Life(x, y);
11944     else if (element == EL_EXIT_CLOSED)
11945       CheckExit(x, y);
11946     else if (element == EL_EM_EXIT_CLOSED)
11947       CheckExitEM(x, y);
11948     else if (element == EL_STEEL_EXIT_CLOSED)
11949       CheckExitSteel(x, y);
11950     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11951       CheckExitSteelEM(x, y);
11952     else if (element == EL_SP_EXIT_CLOSED)
11953       CheckExitSP(x, y);
11954     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11955              element == EL_EXPANDABLE_STEELWALL_GROWING)
11956       MauerWaechst(x, y);
11957     else if (element == EL_EXPANDABLE_WALL ||
11958              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11959              element == EL_EXPANDABLE_WALL_VERTICAL ||
11960              element == EL_EXPANDABLE_WALL_ANY ||
11961              element == EL_BD_EXPANDABLE_WALL)
11962       MauerAbleger(x, y);
11963     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11964              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11965              element == EL_EXPANDABLE_STEELWALL_ANY)
11966       MauerAblegerStahl(x, y);
11967     else if (element == EL_FLAMES)
11968       CheckForDragon(x, y);
11969     else if (element == EL_EXPLOSION)
11970       ; /* drawing of correct explosion animation is handled separately */
11971     else if (element == EL_ELEMENT_SNAPPING ||
11972              element == EL_DIAGONAL_SHRINKING ||
11973              element == EL_DIAGONAL_GROWING)
11974     {
11975       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11976
11977       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11978     }
11979     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11980       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11981
11982     if (IS_BELT_ACTIVE(element))
11983       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11984
11985     if (game.magic_wall_active)
11986     {
11987       int jx = local_player->jx, jy = local_player->jy;
11988
11989       /* play the element sound at the position nearest to the player */
11990       if ((element == EL_MAGIC_WALL_FULL ||
11991            element == EL_MAGIC_WALL_ACTIVE ||
11992            element == EL_MAGIC_WALL_EMPTYING ||
11993            element == EL_BD_MAGIC_WALL_FULL ||
11994            element == EL_BD_MAGIC_WALL_ACTIVE ||
11995            element == EL_BD_MAGIC_WALL_EMPTYING ||
11996            element == EL_DC_MAGIC_WALL_FULL ||
11997            element == EL_DC_MAGIC_WALL_ACTIVE ||
11998            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11999           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12000       {
12001         magic_wall_x = x;
12002         magic_wall_y = y;
12003       }
12004     }
12005   }
12006
12007 #if USE_NEW_AMOEBA_CODE
12008   /* new experimental amoeba growth stuff */
12009   if (!(FrameCounter % 8))
12010   {
12011     static unsigned int random = 1684108901;
12012
12013     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12014     {
12015       x = RND(lev_fieldx);
12016       y = RND(lev_fieldy);
12017       element = Feld[x][y];
12018
12019       if (!IS_PLAYER(x,y) &&
12020           (element == EL_EMPTY ||
12021            CAN_GROW_INTO(element) ||
12022            element == EL_QUICKSAND_EMPTY ||
12023            element == EL_QUICKSAND_FAST_EMPTY ||
12024            element == EL_ACID_SPLASH_LEFT ||
12025            element == EL_ACID_SPLASH_RIGHT))
12026       {
12027         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12028             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12029             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12030             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12031           Feld[x][y] = EL_AMOEBA_DROP;
12032       }
12033
12034       random = random * 129 + 1;
12035     }
12036   }
12037 #endif
12038
12039   game.explosions_delayed = FALSE;
12040
12041   SCAN_PLAYFIELD(x, y)
12042   {
12043     element = Feld[x][y];
12044
12045     if (ExplodeField[x][y])
12046       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12047     else if (element == EL_EXPLOSION)
12048       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12049
12050     ExplodeField[x][y] = EX_TYPE_NONE;
12051   }
12052
12053   game.explosions_delayed = TRUE;
12054
12055   if (game.magic_wall_active)
12056   {
12057     if (!(game.magic_wall_time_left % 4))
12058     {
12059       int element = Feld[magic_wall_x][magic_wall_y];
12060
12061       if (element == EL_BD_MAGIC_WALL_FULL ||
12062           element == EL_BD_MAGIC_WALL_ACTIVE ||
12063           element == EL_BD_MAGIC_WALL_EMPTYING)
12064         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12065       else if (element == EL_DC_MAGIC_WALL_FULL ||
12066                element == EL_DC_MAGIC_WALL_ACTIVE ||
12067                element == EL_DC_MAGIC_WALL_EMPTYING)
12068         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12069       else
12070         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12071     }
12072
12073     if (game.magic_wall_time_left > 0)
12074     {
12075       game.magic_wall_time_left--;
12076
12077       if (!game.magic_wall_time_left)
12078       {
12079         SCAN_PLAYFIELD(x, y)
12080         {
12081           element = Feld[x][y];
12082
12083           if (element == EL_MAGIC_WALL_ACTIVE ||
12084               element == EL_MAGIC_WALL_FULL)
12085           {
12086             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12087             TEST_DrawLevelField(x, y);
12088           }
12089           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12090                    element == EL_BD_MAGIC_WALL_FULL)
12091           {
12092             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12093             TEST_DrawLevelField(x, y);
12094           }
12095           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12096                    element == EL_DC_MAGIC_WALL_FULL)
12097           {
12098             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12099             TEST_DrawLevelField(x, y);
12100           }
12101         }
12102
12103         game.magic_wall_active = FALSE;
12104       }
12105     }
12106   }
12107
12108   if (game.light_time_left > 0)
12109   {
12110     game.light_time_left--;
12111
12112     if (game.light_time_left == 0)
12113       RedrawAllLightSwitchesAndInvisibleElements();
12114   }
12115
12116   if (game.timegate_time_left > 0)
12117   {
12118     game.timegate_time_left--;
12119
12120     if (game.timegate_time_left == 0)
12121       CloseAllOpenTimegates();
12122   }
12123
12124   if (game.lenses_time_left > 0)
12125   {
12126     game.lenses_time_left--;
12127
12128     if (game.lenses_time_left == 0)
12129       RedrawAllInvisibleElementsForLenses();
12130   }
12131
12132   if (game.magnify_time_left > 0)
12133   {
12134     game.magnify_time_left--;
12135
12136     if (game.magnify_time_left == 0)
12137       RedrawAllInvisibleElementsForMagnifier();
12138   }
12139
12140   for (i = 0; i < MAX_PLAYERS; i++)
12141   {
12142     struct PlayerInfo *player = &stored_player[i];
12143
12144     if (SHIELD_ON(player))
12145     {
12146       if (player->shield_deadly_time_left)
12147         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12148       else if (player->shield_normal_time_left)
12149         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12150     }
12151   }
12152
12153 #if USE_DELAYED_GFX_REDRAW
12154   SCAN_PLAYFIELD(x, y)
12155   {
12156     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12157     {
12158       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12159          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12160
12161       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12162         DrawLevelField(x, y);
12163
12164       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12165         DrawLevelFieldCrumbled(x, y);
12166
12167       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12168         DrawLevelFieldCrumbledNeighbours(x, y);
12169
12170       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12171         DrawTwinkleOnField(x, y);
12172     }
12173
12174     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12175   }
12176 #endif
12177
12178   DrawAllPlayers();
12179   PlayAllPlayersSound();
12180
12181   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12182   {
12183     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12184
12185     local_player->show_envelope = 0;
12186   }
12187
12188   /* use random number generator in every frame to make it less predictable */
12189   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12190     RND(1);
12191 }
12192
12193 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12194 {
12195   int min_x = x, min_y = y, max_x = x, max_y = y;
12196   int i;
12197
12198   for (i = 0; i < MAX_PLAYERS; i++)
12199   {
12200     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12201
12202     if (!stored_player[i].active || &stored_player[i] == player)
12203       continue;
12204
12205     min_x = MIN(min_x, jx);
12206     min_y = MIN(min_y, jy);
12207     max_x = MAX(max_x, jx);
12208     max_y = MAX(max_y, jy);
12209   }
12210
12211   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12212 }
12213
12214 static boolean AllPlayersInVisibleScreen()
12215 {
12216   int i;
12217
12218   for (i = 0; i < MAX_PLAYERS; i++)
12219   {
12220     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12221
12222     if (!stored_player[i].active)
12223       continue;
12224
12225     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12226       return FALSE;
12227   }
12228
12229   return TRUE;
12230 }
12231
12232 void ScrollLevel(int dx, int dy)
12233 {
12234   int scroll_offset = 2 * TILEX_VAR;
12235   int x, y;
12236
12237   BlitBitmap(drawto_field, drawto_field,
12238              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12239              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12240              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12241              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12242              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12243              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12244
12245   if (dx != 0)
12246   {
12247     x = (dx == 1 ? BX1 : BX2);
12248     for (y = BY1; y <= BY2; y++)
12249       DrawScreenField(x, y);
12250   }
12251
12252   if (dy != 0)
12253   {
12254     y = (dy == 1 ? BY1 : BY2);
12255     for (x = BX1; x <= BX2; x++)
12256       DrawScreenField(x, y);
12257   }
12258
12259   redraw_mask |= REDRAW_FIELD;
12260 }
12261
12262 static boolean canFallDown(struct PlayerInfo *player)
12263 {
12264   int jx = player->jx, jy = player->jy;
12265
12266   return (IN_LEV_FIELD(jx, jy + 1) &&
12267           (IS_FREE(jx, jy + 1) ||
12268            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12269           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12270           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12271 }
12272
12273 static boolean canPassField(int x, int y, int move_dir)
12274 {
12275   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12276   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12277   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12278   int nextx = x + dx;
12279   int nexty = y + dy;
12280   int element = Feld[x][y];
12281
12282   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12283           !CAN_MOVE(element) &&
12284           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12285           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12286           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12287 }
12288
12289 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12290 {
12291   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12292   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12293   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12294   int newx = x + dx;
12295   int newy = y + dy;
12296
12297   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12298           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12299           (IS_DIGGABLE(Feld[newx][newy]) ||
12300            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12301            canPassField(newx, newy, move_dir)));
12302 }
12303
12304 static void CheckGravityMovement(struct PlayerInfo *player)
12305 {
12306   if (player->gravity && !player->programmed_action)
12307   {
12308     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12309     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12310     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12311     int jx = player->jx, jy = player->jy;
12312     boolean player_is_moving_to_valid_field =
12313       (!player_is_snapping &&
12314        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12315         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12316     boolean player_can_fall_down = canFallDown(player);
12317
12318     if (player_can_fall_down &&
12319         !player_is_moving_to_valid_field)
12320       player->programmed_action = MV_DOWN;
12321   }
12322 }
12323
12324 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12325 {
12326   return CheckGravityMovement(player);
12327
12328   if (player->gravity && !player->programmed_action)
12329   {
12330     int jx = player->jx, jy = player->jy;
12331     boolean field_under_player_is_free =
12332       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12333     boolean player_is_standing_on_valid_field =
12334       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12335        (IS_WALKABLE(Feld[jx][jy]) &&
12336         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12337
12338     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12339       player->programmed_action = MV_DOWN;
12340   }
12341 }
12342
12343 /*
12344   MovePlayerOneStep()
12345   -----------------------------------------------------------------------------
12346   dx, dy:               direction (non-diagonal) to try to move the player to
12347   real_dx, real_dy:     direction as read from input device (can be diagonal)
12348 */
12349
12350 boolean MovePlayerOneStep(struct PlayerInfo *player,
12351                           int dx, int dy, int real_dx, int real_dy)
12352 {
12353   int jx = player->jx, jy = player->jy;
12354   int new_jx = jx + dx, new_jy = jy + dy;
12355   int can_move;
12356   boolean player_can_move = !player->cannot_move;
12357
12358   if (!player->active || (!dx && !dy))
12359     return MP_NO_ACTION;
12360
12361   player->MovDir = (dx < 0 ? MV_LEFT :
12362                     dx > 0 ? MV_RIGHT :
12363                     dy < 0 ? MV_UP :
12364                     dy > 0 ? MV_DOWN :  MV_NONE);
12365
12366   if (!IN_LEV_FIELD(new_jx, new_jy))
12367     return MP_NO_ACTION;
12368
12369   if (!player_can_move)
12370   {
12371     if (player->MovPos == 0)
12372     {
12373       player->is_moving = FALSE;
12374       player->is_digging = FALSE;
12375       player->is_collecting = FALSE;
12376       player->is_snapping = FALSE;
12377       player->is_pushing = FALSE;
12378     }
12379   }
12380
12381   if (!network.enabled && game.centered_player_nr == -1 &&
12382       !AllPlayersInSight(player, new_jx, new_jy))
12383     return MP_NO_ACTION;
12384
12385   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12386   if (can_move != MP_MOVING)
12387     return can_move;
12388
12389   /* check if DigField() has caused relocation of the player */
12390   if (player->jx != jx || player->jy != jy)
12391     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12392
12393   StorePlayer[jx][jy] = 0;
12394   player->last_jx = jx;
12395   player->last_jy = jy;
12396   player->jx = new_jx;
12397   player->jy = new_jy;
12398   StorePlayer[new_jx][new_jy] = player->element_nr;
12399
12400   if (player->move_delay_value_next != -1)
12401   {
12402     player->move_delay_value = player->move_delay_value_next;
12403     player->move_delay_value_next = -1;
12404   }
12405
12406   player->MovPos =
12407     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12408
12409   player->step_counter++;
12410
12411   PlayerVisit[jx][jy] = FrameCounter;
12412
12413   player->is_moving = TRUE;
12414
12415 #if 1
12416   /* should better be called in MovePlayer(), but this breaks some tapes */
12417   ScrollPlayer(player, SCROLL_INIT);
12418 #endif
12419
12420   return MP_MOVING;
12421 }
12422
12423 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12424 {
12425   int jx = player->jx, jy = player->jy;
12426   int old_jx = jx, old_jy = jy;
12427   int moved = MP_NO_ACTION;
12428
12429   if (!player->active)
12430     return FALSE;
12431
12432   if (!dx && !dy)
12433   {
12434     if (player->MovPos == 0)
12435     {
12436       player->is_moving = FALSE;
12437       player->is_digging = FALSE;
12438       player->is_collecting = FALSE;
12439       player->is_snapping = FALSE;
12440       player->is_pushing = FALSE;
12441     }
12442
12443     return FALSE;
12444   }
12445
12446   if (player->move_delay > 0)
12447     return FALSE;
12448
12449   player->move_delay = -1;              /* set to "uninitialized" value */
12450
12451   /* store if player is automatically moved to next field */
12452   player->is_auto_moving = (player->programmed_action != MV_NONE);
12453
12454   /* remove the last programmed player action */
12455   player->programmed_action = 0;
12456
12457   if (player->MovPos)
12458   {
12459     /* should only happen if pre-1.2 tape recordings are played */
12460     /* this is only for backward compatibility */
12461
12462     int original_move_delay_value = player->move_delay_value;
12463
12464 #if DEBUG
12465     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12466            tape.counter);
12467 #endif
12468
12469     /* scroll remaining steps with finest movement resolution */
12470     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12471
12472     while (player->MovPos)
12473     {
12474       ScrollPlayer(player, SCROLL_GO_ON);
12475       ScrollScreen(NULL, SCROLL_GO_ON);
12476
12477       AdvanceFrameAndPlayerCounters(player->index_nr);
12478
12479       DrawAllPlayers();
12480       BackToFront_WithFrameDelay(0);
12481     }
12482
12483     player->move_delay_value = original_move_delay_value;
12484   }
12485
12486   player->is_active = FALSE;
12487
12488   if (player->last_move_dir & MV_HORIZONTAL)
12489   {
12490     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12491       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12492   }
12493   else
12494   {
12495     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12496       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12497   }
12498
12499   if (!moved && !player->is_active)
12500   {
12501     player->is_moving = FALSE;
12502     player->is_digging = FALSE;
12503     player->is_collecting = FALSE;
12504     player->is_snapping = FALSE;
12505     player->is_pushing = FALSE;
12506   }
12507
12508   jx = player->jx;
12509   jy = player->jy;
12510
12511   if (moved & MP_MOVING && !ScreenMovPos &&
12512       (player->index_nr == game.centered_player_nr ||
12513        game.centered_player_nr == -1))
12514   {
12515     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12516     int offset = game.scroll_delay_value;
12517
12518     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12519     {
12520       /* actual player has left the screen -- scroll in that direction */
12521       if (jx != old_jx)         /* player has moved horizontally */
12522         scroll_x += (jx - old_jx);
12523       else                      /* player has moved vertically */
12524         scroll_y += (jy - old_jy);
12525     }
12526     else
12527     {
12528       if (jx != old_jx)         /* player has moved horizontally */
12529       {
12530         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12531             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12532           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12533
12534         /* don't scroll over playfield boundaries */
12535         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12536           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12537
12538         /* don't scroll more than one field at a time */
12539         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12540
12541         /* don't scroll against the player's moving direction */
12542         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12543             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12544           scroll_x = old_scroll_x;
12545       }
12546       else                      /* player has moved vertically */
12547       {
12548         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12549             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12550           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12551
12552         /* don't scroll over playfield boundaries */
12553         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12554           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12555
12556         /* don't scroll more than one field at a time */
12557         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12558
12559         /* don't scroll against the player's moving direction */
12560         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12561             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12562           scroll_y = old_scroll_y;
12563       }
12564     }
12565
12566     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12567     {
12568       if (!network.enabled && game.centered_player_nr == -1 &&
12569           !AllPlayersInVisibleScreen())
12570       {
12571         scroll_x = old_scroll_x;
12572         scroll_y = old_scroll_y;
12573       }
12574       else
12575       {
12576         ScrollScreen(player, SCROLL_INIT);
12577         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12578       }
12579     }
12580   }
12581
12582   player->StepFrame = 0;
12583
12584   if (moved & MP_MOVING)
12585   {
12586     if (old_jx != jx && old_jy == jy)
12587       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12588     else if (old_jx == jx && old_jy != jy)
12589       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12590
12591     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12592
12593     player->last_move_dir = player->MovDir;
12594     player->is_moving = TRUE;
12595     player->is_snapping = FALSE;
12596     player->is_switching = FALSE;
12597     player->is_dropping = FALSE;
12598     player->is_dropping_pressed = FALSE;
12599     player->drop_pressed_delay = 0;
12600
12601 #if 0
12602     /* should better be called here than above, but this breaks some tapes */
12603     ScrollPlayer(player, SCROLL_INIT);
12604 #endif
12605   }
12606   else
12607   {
12608     CheckGravityMovementWhenNotMoving(player);
12609
12610     player->is_moving = FALSE;
12611
12612     /* at this point, the player is allowed to move, but cannot move right now
12613        (e.g. because of something blocking the way) -- ensure that the player
12614        is also allowed to move in the next frame (in old versions before 3.1.1,
12615        the player was forced to wait again for eight frames before next try) */
12616
12617     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12618       player->move_delay = 0;   /* allow direct movement in the next frame */
12619   }
12620
12621   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12622     player->move_delay = player->move_delay_value;
12623
12624   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12625   {
12626     TestIfPlayerTouchesBadThing(jx, jy);
12627     TestIfPlayerTouchesCustomElement(jx, jy);
12628   }
12629
12630   if (!player->active)
12631     RemovePlayer(player);
12632
12633   return moved;
12634 }
12635
12636 void ScrollPlayer(struct PlayerInfo *player, int mode)
12637 {
12638   int jx = player->jx, jy = player->jy;
12639   int last_jx = player->last_jx, last_jy = player->last_jy;
12640   int move_stepsize = TILEX / player->move_delay_value;
12641
12642   if (!player->active)
12643     return;
12644
12645   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12646     return;
12647
12648   if (mode == SCROLL_INIT)
12649   {
12650     player->actual_frame_counter = FrameCounter;
12651     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12652
12653     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12654         Feld[last_jx][last_jy] == EL_EMPTY)
12655     {
12656       int last_field_block_delay = 0;   /* start with no blocking at all */
12657       int block_delay_adjustment = player->block_delay_adjustment;
12658
12659       /* if player blocks last field, add delay for exactly one move */
12660       if (player->block_last_field)
12661       {
12662         last_field_block_delay += player->move_delay_value;
12663
12664         /* when blocking enabled, prevent moving up despite gravity */
12665         if (player->gravity && player->MovDir == MV_UP)
12666           block_delay_adjustment = -1;
12667       }
12668
12669       /* add block delay adjustment (also possible when not blocking) */
12670       last_field_block_delay += block_delay_adjustment;
12671
12672       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12673       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12674     }
12675
12676     if (player->MovPos != 0)    /* player has not yet reached destination */
12677       return;
12678   }
12679   else if (!FrameReached(&player->actual_frame_counter, 1))
12680     return;
12681
12682   if (player->MovPos != 0)
12683   {
12684     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12685     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12686
12687     /* before DrawPlayer() to draw correct player graphic for this case */
12688     if (player->MovPos == 0)
12689       CheckGravityMovement(player);
12690   }
12691
12692   if (player->MovPos == 0)      /* player reached destination field */
12693   {
12694     if (player->move_delay_reset_counter > 0)
12695     {
12696       player->move_delay_reset_counter--;
12697
12698       if (player->move_delay_reset_counter == 0)
12699       {
12700         /* continue with normal speed after quickly moving through gate */
12701         HALVE_PLAYER_SPEED(player);
12702
12703         /* be able to make the next move without delay */
12704         player->move_delay = 0;
12705       }
12706     }
12707
12708     player->last_jx = jx;
12709     player->last_jy = jy;
12710
12711     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12712         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12713         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12714         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12715         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12716         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12717         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12718         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12719     {
12720       DrawPlayer(player);       /* needed here only to cleanup last field */
12721       RemovePlayer(player);
12722
12723       if (local_player->friends_still_needed == 0 ||
12724           IS_SP_ELEMENT(Feld[jx][jy]))
12725         PlayerWins(player);
12726     }
12727
12728     /* this breaks one level: "machine", level 000 */
12729     {
12730       int move_direction = player->MovDir;
12731       int enter_side = MV_DIR_OPPOSITE(move_direction);
12732       int leave_side = move_direction;
12733       int old_jx = last_jx;
12734       int old_jy = last_jy;
12735       int old_element = Feld[old_jx][old_jy];
12736       int new_element = Feld[jx][jy];
12737
12738       if (IS_CUSTOM_ELEMENT(old_element))
12739         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12740                                    CE_LEFT_BY_PLAYER,
12741                                    player->index_bit, leave_side);
12742
12743       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12744                                           CE_PLAYER_LEAVES_X,
12745                                           player->index_bit, leave_side);
12746
12747       if (IS_CUSTOM_ELEMENT(new_element))
12748         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12749                                    player->index_bit, enter_side);
12750
12751       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12752                                           CE_PLAYER_ENTERS_X,
12753                                           player->index_bit, enter_side);
12754
12755       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12756                                         CE_MOVE_OF_X, move_direction);
12757     }
12758
12759     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12760     {
12761       TestIfPlayerTouchesBadThing(jx, jy);
12762       TestIfPlayerTouchesCustomElement(jx, jy);
12763
12764       /* needed because pushed element has not yet reached its destination,
12765          so it would trigger a change event at its previous field location */
12766       if (!player->is_pushing)
12767         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12768
12769       if (!player->active)
12770         RemovePlayer(player);
12771     }
12772
12773     if (!local_player->LevelSolved && level.use_step_counter)
12774     {
12775       int i;
12776
12777       TimePlayed++;
12778
12779       if (TimeLeft > 0)
12780       {
12781         TimeLeft--;
12782
12783         if (TimeLeft <= 10 && setup.time_limit)
12784           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12785
12786         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12787
12788         DisplayGameControlValues();
12789
12790         if (!TimeLeft && setup.time_limit)
12791           for (i = 0; i < MAX_PLAYERS; i++)
12792             KillPlayer(&stored_player[i]);
12793       }
12794       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12795       {
12796         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12797
12798         DisplayGameControlValues();
12799       }
12800     }
12801
12802     if (tape.single_step && tape.recording && !tape.pausing &&
12803         !player->programmed_action)
12804       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12805
12806     if (!player->programmed_action)
12807       CheckSaveEngineSnapshot(player);
12808   }
12809 }
12810
12811 void ScrollScreen(struct PlayerInfo *player, int mode)
12812 {
12813   static unsigned int screen_frame_counter = 0;
12814
12815   if (mode == SCROLL_INIT)
12816   {
12817     /* set scrolling step size according to actual player's moving speed */
12818     ScrollStepSize = TILEX / player->move_delay_value;
12819
12820     screen_frame_counter = FrameCounter;
12821     ScreenMovDir = player->MovDir;
12822     ScreenMovPos = player->MovPos;
12823     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12824     return;
12825   }
12826   else if (!FrameReached(&screen_frame_counter, 1))
12827     return;
12828
12829   if (ScreenMovPos)
12830   {
12831     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12832     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12833     redraw_mask |= REDRAW_FIELD;
12834   }
12835   else
12836     ScreenMovDir = MV_NONE;
12837 }
12838
12839 void TestIfPlayerTouchesCustomElement(int x, int y)
12840 {
12841   static int xy[4][2] =
12842   {
12843     { 0, -1 },
12844     { -1, 0 },
12845     { +1, 0 },
12846     { 0, +1 }
12847   };
12848   static int trigger_sides[4][2] =
12849   {
12850     /* center side       border side */
12851     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12852     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12853     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12854     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12855   };
12856   static int touch_dir[4] =
12857   {
12858     MV_LEFT | MV_RIGHT,
12859     MV_UP   | MV_DOWN,
12860     MV_UP   | MV_DOWN,
12861     MV_LEFT | MV_RIGHT
12862   };
12863   int center_element = Feld[x][y];      /* should always be non-moving! */
12864   int i;
12865
12866   for (i = 0; i < NUM_DIRECTIONS; i++)
12867   {
12868     int xx = x + xy[i][0];
12869     int yy = y + xy[i][1];
12870     int center_side = trigger_sides[i][0];
12871     int border_side = trigger_sides[i][1];
12872     int border_element;
12873
12874     if (!IN_LEV_FIELD(xx, yy))
12875       continue;
12876
12877     if (IS_PLAYER(x, y))                /* player found at center element */
12878     {
12879       struct PlayerInfo *player = PLAYERINFO(x, y);
12880
12881       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12882         border_element = Feld[xx][yy];          /* may be moving! */
12883       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12884         border_element = Feld[xx][yy];
12885       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12886         border_element = MovingOrBlocked2Element(xx, yy);
12887       else
12888         continue;               /* center and border element do not touch */
12889
12890       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12891                                  player->index_bit, border_side);
12892       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12893                                           CE_PLAYER_TOUCHES_X,
12894                                           player->index_bit, border_side);
12895
12896       {
12897         /* use player element that is initially defined in the level playfield,
12898            not the player element that corresponds to the runtime player number
12899            (example: a level that contains EL_PLAYER_3 as the only player would
12900            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12901         int player_element = PLAYERINFO(x, y)->initial_element;
12902
12903         CheckElementChangeBySide(xx, yy, border_element, player_element,
12904                                  CE_TOUCHING_X, border_side);
12905       }
12906     }
12907     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12908     {
12909       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12910
12911       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12912       {
12913         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12914           continue;             /* center and border element do not touch */
12915       }
12916
12917       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12918                                  player->index_bit, center_side);
12919       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12920                                           CE_PLAYER_TOUCHES_X,
12921                                           player->index_bit, center_side);
12922
12923       {
12924         /* use player element that is initially defined in the level playfield,
12925            not the player element that corresponds to the runtime player number
12926            (example: a level that contains EL_PLAYER_3 as the only player would
12927            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12928         int player_element = PLAYERINFO(xx, yy)->initial_element;
12929
12930         CheckElementChangeBySide(x, y, center_element, player_element,
12931                                  CE_TOUCHING_X, center_side);
12932       }
12933
12934       break;
12935     }
12936   }
12937 }
12938
12939 void TestIfElementTouchesCustomElement(int x, int y)
12940 {
12941   static int xy[4][2] =
12942   {
12943     { 0, -1 },
12944     { -1, 0 },
12945     { +1, 0 },
12946     { 0, +1 }
12947   };
12948   static int trigger_sides[4][2] =
12949   {
12950     /* center side      border side */
12951     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12952     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12953     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12954     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12955   };
12956   static int touch_dir[4] =
12957   {
12958     MV_LEFT | MV_RIGHT,
12959     MV_UP   | MV_DOWN,
12960     MV_UP   | MV_DOWN,
12961     MV_LEFT | MV_RIGHT
12962   };
12963   boolean change_center_element = FALSE;
12964   int center_element = Feld[x][y];      /* should always be non-moving! */
12965   int border_element_old[NUM_DIRECTIONS];
12966   int i;
12967
12968   for (i = 0; i < NUM_DIRECTIONS; i++)
12969   {
12970     int xx = x + xy[i][0];
12971     int yy = y + xy[i][1];
12972     int border_element;
12973
12974     border_element_old[i] = -1;
12975
12976     if (!IN_LEV_FIELD(xx, yy))
12977       continue;
12978
12979     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12980       border_element = Feld[xx][yy];    /* may be moving! */
12981     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12982       border_element = Feld[xx][yy];
12983     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12984       border_element = MovingOrBlocked2Element(xx, yy);
12985     else
12986       continue;                 /* center and border element do not touch */
12987
12988     border_element_old[i] = border_element;
12989   }
12990
12991   for (i = 0; i < NUM_DIRECTIONS; i++)
12992   {
12993     int xx = x + xy[i][0];
12994     int yy = y + xy[i][1];
12995     int center_side = trigger_sides[i][0];
12996     int border_element = border_element_old[i];
12997
12998     if (border_element == -1)
12999       continue;
13000
13001     /* check for change of border element */
13002     CheckElementChangeBySide(xx, yy, border_element, center_element,
13003                              CE_TOUCHING_X, center_side);
13004
13005     /* (center element cannot be player, so we dont have to check this here) */
13006   }
13007
13008   for (i = 0; i < NUM_DIRECTIONS; i++)
13009   {
13010     int xx = x + xy[i][0];
13011     int yy = y + xy[i][1];
13012     int border_side = trigger_sides[i][1];
13013     int border_element = border_element_old[i];
13014
13015     if (border_element == -1)
13016       continue;
13017
13018     /* check for change of center element (but change it only once) */
13019     if (!change_center_element)
13020       change_center_element =
13021         CheckElementChangeBySide(x, y, center_element, border_element,
13022                                  CE_TOUCHING_X, border_side);
13023
13024     if (IS_PLAYER(xx, yy))
13025     {
13026       /* use player element that is initially defined in the level playfield,
13027          not the player element that corresponds to the runtime player number
13028          (example: a level that contains EL_PLAYER_3 as the only player would
13029          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13030       int player_element = PLAYERINFO(xx, yy)->initial_element;
13031
13032       CheckElementChangeBySide(x, y, center_element, player_element,
13033                                CE_TOUCHING_X, border_side);
13034     }
13035   }
13036 }
13037
13038 void TestIfElementHitsCustomElement(int x, int y, int direction)
13039 {
13040   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13041   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13042   int hitx = x + dx, hity = y + dy;
13043   int hitting_element = Feld[x][y];
13044   int touched_element;
13045
13046   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13047     return;
13048
13049   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13050                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13051
13052   if (IN_LEV_FIELD(hitx, hity))
13053   {
13054     int opposite_direction = MV_DIR_OPPOSITE(direction);
13055     int hitting_side = direction;
13056     int touched_side = opposite_direction;
13057     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13058                           MovDir[hitx][hity] != direction ||
13059                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13060
13061     object_hit = TRUE;
13062
13063     if (object_hit)
13064     {
13065       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13066                                CE_HITTING_X, touched_side);
13067
13068       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13069                                CE_HIT_BY_X, hitting_side);
13070
13071       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13072                                CE_HIT_BY_SOMETHING, opposite_direction);
13073
13074       if (IS_PLAYER(hitx, hity))
13075       {
13076         /* use player element that is initially defined in the level playfield,
13077            not the player element that corresponds to the runtime player number
13078            (example: a level that contains EL_PLAYER_3 as the only player would
13079            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13080         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13081
13082         CheckElementChangeBySide(x, y, hitting_element, player_element,
13083                                  CE_HITTING_X, touched_side);
13084       }
13085     }
13086   }
13087
13088   /* "hitting something" is also true when hitting the playfield border */
13089   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13090                            CE_HITTING_SOMETHING, direction);
13091 }
13092
13093 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13094 {
13095   int i, kill_x = -1, kill_y = -1;
13096
13097   int bad_element = -1;
13098   static int test_xy[4][2] =
13099   {
13100     { 0, -1 },
13101     { -1, 0 },
13102     { +1, 0 },
13103     { 0, +1 }
13104   };
13105   static int test_dir[4] =
13106   {
13107     MV_UP,
13108     MV_LEFT,
13109     MV_RIGHT,
13110     MV_DOWN
13111   };
13112
13113   for (i = 0; i < NUM_DIRECTIONS; i++)
13114   {
13115     int test_x, test_y, test_move_dir, test_element;
13116
13117     test_x = good_x + test_xy[i][0];
13118     test_y = good_y + test_xy[i][1];
13119
13120     if (!IN_LEV_FIELD(test_x, test_y))
13121       continue;
13122
13123     test_move_dir =
13124       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13125
13126     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13127
13128     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13129        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13130     */
13131     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13132         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13133     {
13134       kill_x = test_x;
13135       kill_y = test_y;
13136       bad_element = test_element;
13137
13138       break;
13139     }
13140   }
13141
13142   if (kill_x != -1 || kill_y != -1)
13143   {
13144     if (IS_PLAYER(good_x, good_y))
13145     {
13146       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13147
13148       if (player->shield_deadly_time_left > 0 &&
13149           !IS_INDESTRUCTIBLE(bad_element))
13150         Bang(kill_x, kill_y);
13151       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13152         KillPlayer(player);
13153     }
13154     else
13155       Bang(good_x, good_y);
13156   }
13157 }
13158
13159 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13160 {
13161   int i, kill_x = -1, kill_y = -1;
13162   int bad_element = Feld[bad_x][bad_y];
13163   static int test_xy[4][2] =
13164   {
13165     { 0, -1 },
13166     { -1, 0 },
13167     { +1, 0 },
13168     { 0, +1 }
13169   };
13170   static int touch_dir[4] =
13171   {
13172     MV_LEFT | MV_RIGHT,
13173     MV_UP   | MV_DOWN,
13174     MV_UP   | MV_DOWN,
13175     MV_LEFT | MV_RIGHT
13176   };
13177   static int test_dir[4] =
13178   {
13179     MV_UP,
13180     MV_LEFT,
13181     MV_RIGHT,
13182     MV_DOWN
13183   };
13184
13185   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13186     return;
13187
13188   for (i = 0; i < NUM_DIRECTIONS; i++)
13189   {
13190     int test_x, test_y, test_move_dir, test_element;
13191
13192     test_x = bad_x + test_xy[i][0];
13193     test_y = bad_y + test_xy[i][1];
13194
13195     if (!IN_LEV_FIELD(test_x, test_y))
13196       continue;
13197
13198     test_move_dir =
13199       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13200
13201     test_element = Feld[test_x][test_y];
13202
13203     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13204        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13205     */
13206     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13207         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13208     {
13209       /* good thing is player or penguin that does not move away */
13210       if (IS_PLAYER(test_x, test_y))
13211       {
13212         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13213
13214         if (bad_element == EL_ROBOT && player->is_moving)
13215           continue;     /* robot does not kill player if he is moving */
13216
13217         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13218         {
13219           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13220             continue;           /* center and border element do not touch */
13221         }
13222
13223         kill_x = test_x;
13224         kill_y = test_y;
13225
13226         break;
13227       }
13228       else if (test_element == EL_PENGUIN)
13229       {
13230         kill_x = test_x;
13231         kill_y = test_y;
13232
13233         break;
13234       }
13235     }
13236   }
13237
13238   if (kill_x != -1 || kill_y != -1)
13239   {
13240     if (IS_PLAYER(kill_x, kill_y))
13241     {
13242       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13243
13244       if (player->shield_deadly_time_left > 0 &&
13245           !IS_INDESTRUCTIBLE(bad_element))
13246         Bang(bad_x, bad_y);
13247       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13248         KillPlayer(player);
13249     }
13250     else
13251       Bang(kill_x, kill_y);
13252   }
13253 }
13254
13255 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13256 {
13257   int bad_element = Feld[bad_x][bad_y];
13258   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13259   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13260   int test_x = bad_x + dx, test_y = bad_y + dy;
13261   int test_move_dir, test_element;
13262   int kill_x = -1, kill_y = -1;
13263
13264   if (!IN_LEV_FIELD(test_x, test_y))
13265     return;
13266
13267   test_move_dir =
13268     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13269
13270   test_element = Feld[test_x][test_y];
13271
13272   if (test_move_dir != bad_move_dir)
13273   {
13274     /* good thing can be player or penguin that does not move away */
13275     if (IS_PLAYER(test_x, test_y))
13276     {
13277       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13278
13279       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13280          player as being hit when he is moving towards the bad thing, because
13281          the "get hit by" condition would be lost after the player stops) */
13282       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13283         return;         /* player moves away from bad thing */
13284
13285       kill_x = test_x;
13286       kill_y = test_y;
13287     }
13288     else if (test_element == EL_PENGUIN)
13289     {
13290       kill_x = test_x;
13291       kill_y = test_y;
13292     }
13293   }
13294
13295   if (kill_x != -1 || kill_y != -1)
13296   {
13297     if (IS_PLAYER(kill_x, kill_y))
13298     {
13299       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13300
13301       if (player->shield_deadly_time_left > 0 &&
13302           !IS_INDESTRUCTIBLE(bad_element))
13303         Bang(bad_x, bad_y);
13304       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13305         KillPlayer(player);
13306     }
13307     else
13308       Bang(kill_x, kill_y);
13309   }
13310 }
13311
13312 void TestIfPlayerTouchesBadThing(int x, int y)
13313 {
13314   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13315 }
13316
13317 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13318 {
13319   TestIfGoodThingHitsBadThing(x, y, move_dir);
13320 }
13321
13322 void TestIfBadThingTouchesPlayer(int x, int y)
13323 {
13324   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13325 }
13326
13327 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13328 {
13329   TestIfBadThingHitsGoodThing(x, y, move_dir);
13330 }
13331
13332 void TestIfFriendTouchesBadThing(int x, int y)
13333 {
13334   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13335 }
13336
13337 void TestIfBadThingTouchesFriend(int x, int y)
13338 {
13339   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13340 }
13341
13342 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13343 {
13344   int i, kill_x = bad_x, kill_y = bad_y;
13345   static int xy[4][2] =
13346   {
13347     { 0, -1 },
13348     { -1, 0 },
13349     { +1, 0 },
13350     { 0, +1 }
13351   };
13352
13353   for (i = 0; i < NUM_DIRECTIONS; i++)
13354   {
13355     int x, y, element;
13356
13357     x = bad_x + xy[i][0];
13358     y = bad_y + xy[i][1];
13359     if (!IN_LEV_FIELD(x, y))
13360       continue;
13361
13362     element = Feld[x][y];
13363     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13364         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13365     {
13366       kill_x = x;
13367       kill_y = y;
13368       break;
13369     }
13370   }
13371
13372   if (kill_x != bad_x || kill_y != bad_y)
13373     Bang(bad_x, bad_y);
13374 }
13375
13376 void KillPlayer(struct PlayerInfo *player)
13377 {
13378   int jx = player->jx, jy = player->jy;
13379
13380   if (!player->active)
13381     return;
13382
13383 #if 0
13384   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13385          player->killed, player->active, player->reanimated);
13386 #endif
13387
13388   /* the following code was introduced to prevent an infinite loop when calling
13389      -> Bang()
13390      -> CheckTriggeredElementChangeExt()
13391      -> ExecuteCustomElementAction()
13392      -> KillPlayer()
13393      -> (infinitely repeating the above sequence of function calls)
13394      which occurs when killing the player while having a CE with the setting
13395      "kill player X when explosion of <player X>"; the solution using a new
13396      field "player->killed" was chosen for backwards compatibility, although
13397      clever use of the fields "player->active" etc. would probably also work */
13398 #if 1
13399   if (player->killed)
13400     return;
13401 #endif
13402
13403   player->killed = TRUE;
13404
13405   /* remove accessible field at the player's position */
13406   Feld[jx][jy] = EL_EMPTY;
13407
13408   /* deactivate shield (else Bang()/Explode() would not work right) */
13409   player->shield_normal_time_left = 0;
13410   player->shield_deadly_time_left = 0;
13411
13412 #if 0
13413   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13414          player->killed, player->active, player->reanimated);
13415 #endif
13416
13417   Bang(jx, jy);
13418
13419 #if 0
13420   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13421          player->killed, player->active, player->reanimated);
13422 #endif
13423
13424   if (player->reanimated)       /* killed player may have been reanimated */
13425     player->killed = player->reanimated = FALSE;
13426   else
13427     BuryPlayer(player);
13428 }
13429
13430 static void KillPlayerUnlessEnemyProtected(int x, int y)
13431 {
13432   if (!PLAYER_ENEMY_PROTECTED(x, y))
13433     KillPlayer(PLAYERINFO(x, y));
13434 }
13435
13436 static void KillPlayerUnlessExplosionProtected(int x, int y)
13437 {
13438   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13439     KillPlayer(PLAYERINFO(x, y));
13440 }
13441
13442 void BuryPlayer(struct PlayerInfo *player)
13443 {
13444   int jx = player->jx, jy = player->jy;
13445
13446   if (!player->active)
13447     return;
13448
13449   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13450   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13451
13452   player->GameOver = TRUE;
13453   RemovePlayer(player);
13454 }
13455
13456 void RemovePlayer(struct PlayerInfo *player)
13457 {
13458   int jx = player->jx, jy = player->jy;
13459   int i, found = FALSE;
13460
13461   player->present = FALSE;
13462   player->active = FALSE;
13463
13464   if (!ExplodeField[jx][jy])
13465     StorePlayer[jx][jy] = 0;
13466
13467   if (player->is_moving)
13468     TEST_DrawLevelField(player->last_jx, player->last_jy);
13469
13470   for (i = 0; i < MAX_PLAYERS; i++)
13471     if (stored_player[i].active)
13472       found = TRUE;
13473
13474   if (!found)
13475     AllPlayersGone = TRUE;
13476
13477   ExitX = ZX = jx;
13478   ExitY = ZY = jy;
13479 }
13480
13481 static void setFieldForSnapping(int x, int y, int element, int direction)
13482 {
13483   struct ElementInfo *ei = &element_info[element];
13484   int direction_bit = MV_DIR_TO_BIT(direction);
13485   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13486   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13487                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13488
13489   Feld[x][y] = EL_ELEMENT_SNAPPING;
13490   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13491
13492   ResetGfxAnimation(x, y);
13493
13494   GfxElement[x][y] = element;
13495   GfxAction[x][y] = action;
13496   GfxDir[x][y] = direction;
13497   GfxFrame[x][y] = -1;
13498 }
13499
13500 /*
13501   =============================================================================
13502   checkDiagonalPushing()
13503   -----------------------------------------------------------------------------
13504   check if diagonal input device direction results in pushing of object
13505   (by checking if the alternative direction is walkable, diggable, ...)
13506   =============================================================================
13507 */
13508
13509 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13510                                     int x, int y, int real_dx, int real_dy)
13511 {
13512   int jx, jy, dx, dy, xx, yy;
13513
13514   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13515     return TRUE;
13516
13517   /* diagonal direction: check alternative direction */
13518   jx = player->jx;
13519   jy = player->jy;
13520   dx = x - jx;
13521   dy = y - jy;
13522   xx = jx + (dx == 0 ? real_dx : 0);
13523   yy = jy + (dy == 0 ? real_dy : 0);
13524
13525   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13526 }
13527
13528 /*
13529   =============================================================================
13530   DigField()
13531   -----------------------------------------------------------------------------
13532   x, y:                 field next to player (non-diagonal) to try to dig to
13533   real_dx, real_dy:     direction as read from input device (can be diagonal)
13534   =============================================================================
13535 */
13536
13537 static int DigField(struct PlayerInfo *player,
13538                     int oldx, int oldy, int x, int y,
13539                     int real_dx, int real_dy, int mode)
13540 {
13541   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13542   boolean player_was_pushing = player->is_pushing;
13543   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13544   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13545   int jx = oldx, jy = oldy;
13546   int dx = x - jx, dy = y - jy;
13547   int nextx = x + dx, nexty = y + dy;
13548   int move_direction = (dx == -1 ? MV_LEFT  :
13549                         dx == +1 ? MV_RIGHT :
13550                         dy == -1 ? MV_UP    :
13551                         dy == +1 ? MV_DOWN  : MV_NONE);
13552   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13553   int dig_side = MV_DIR_OPPOSITE(move_direction);
13554   int old_element = Feld[jx][jy];
13555   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13556   int collect_count;
13557
13558   if (is_player)                /* function can also be called by EL_PENGUIN */
13559   {
13560     if (player->MovPos == 0)
13561     {
13562       player->is_digging = FALSE;
13563       player->is_collecting = FALSE;
13564     }
13565
13566     if (player->MovPos == 0)    /* last pushing move finished */
13567       player->is_pushing = FALSE;
13568
13569     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13570     {
13571       player->is_switching = FALSE;
13572       player->push_delay = -1;
13573
13574       return MP_NO_ACTION;
13575     }
13576   }
13577
13578   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13579     old_element = Back[jx][jy];
13580
13581   /* in case of element dropped at player position, check background */
13582   else if (Back[jx][jy] != EL_EMPTY &&
13583            game.engine_version >= VERSION_IDENT(2,2,0,0))
13584     old_element = Back[jx][jy];
13585
13586   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13587     return MP_NO_ACTION;        /* field has no opening in this direction */
13588
13589   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13590     return MP_NO_ACTION;        /* field has no opening in this direction */
13591
13592   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13593   {
13594     SplashAcid(x, y);
13595
13596     Feld[jx][jy] = player->artwork_element;
13597     InitMovingField(jx, jy, MV_DOWN);
13598     Store[jx][jy] = EL_ACID;
13599     ContinueMoving(jx, jy);
13600     BuryPlayer(player);
13601
13602     return MP_DONT_RUN_INTO;
13603   }
13604
13605   if (player_can_move && DONT_RUN_INTO(element))
13606   {
13607     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13608
13609     return MP_DONT_RUN_INTO;
13610   }
13611
13612   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13613     return MP_NO_ACTION;
13614
13615   collect_count = element_info[element].collect_count_initial;
13616
13617   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13618     return MP_NO_ACTION;
13619
13620   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13621     player_can_move = player_can_move_or_snap;
13622
13623   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13624       game.engine_version >= VERSION_IDENT(2,2,0,0))
13625   {
13626     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13627                                player->index_bit, dig_side);
13628     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13629                                         player->index_bit, dig_side);
13630
13631     if (element == EL_DC_LANDMINE)
13632       Bang(x, y);
13633
13634     if (Feld[x][y] != element)          /* field changed by snapping */
13635       return MP_ACTION;
13636
13637     return MP_NO_ACTION;
13638   }
13639
13640   if (player->gravity && is_player && !player->is_auto_moving &&
13641       canFallDown(player) && move_direction != MV_DOWN &&
13642       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13643     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13644
13645   if (player_can_move &&
13646       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13647   {
13648     int sound_element = SND_ELEMENT(element);
13649     int sound_action = ACTION_WALKING;
13650
13651     if (IS_RND_GATE(element))
13652     {
13653       if (!player->key[RND_GATE_NR(element)])
13654         return MP_NO_ACTION;
13655     }
13656     else if (IS_RND_GATE_GRAY(element))
13657     {
13658       if (!player->key[RND_GATE_GRAY_NR(element)])
13659         return MP_NO_ACTION;
13660     }
13661     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13662     {
13663       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13664         return MP_NO_ACTION;
13665     }
13666     else if (element == EL_EXIT_OPEN ||
13667              element == EL_EM_EXIT_OPEN ||
13668              element == EL_EM_EXIT_OPENING ||
13669              element == EL_STEEL_EXIT_OPEN ||
13670              element == EL_EM_STEEL_EXIT_OPEN ||
13671              element == EL_EM_STEEL_EXIT_OPENING ||
13672              element == EL_SP_EXIT_OPEN ||
13673              element == EL_SP_EXIT_OPENING)
13674     {
13675       sound_action = ACTION_PASSING;    /* player is passing exit */
13676     }
13677     else if (element == EL_EMPTY)
13678     {
13679       sound_action = ACTION_MOVING;             /* nothing to walk on */
13680     }
13681
13682     /* play sound from background or player, whatever is available */
13683     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13684       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13685     else
13686       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13687   }
13688   else if (player_can_move &&
13689            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13690   {
13691     if (!ACCESS_FROM(element, opposite_direction))
13692       return MP_NO_ACTION;      /* field not accessible from this direction */
13693
13694     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13695       return MP_NO_ACTION;
13696
13697     if (IS_EM_GATE(element))
13698     {
13699       if (!player->key[EM_GATE_NR(element)])
13700         return MP_NO_ACTION;
13701     }
13702     else if (IS_EM_GATE_GRAY(element))
13703     {
13704       if (!player->key[EM_GATE_GRAY_NR(element)])
13705         return MP_NO_ACTION;
13706     }
13707     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13708     {
13709       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13710         return MP_NO_ACTION;
13711     }
13712     else if (IS_EMC_GATE(element))
13713     {
13714       if (!player->key[EMC_GATE_NR(element)])
13715         return MP_NO_ACTION;
13716     }
13717     else if (IS_EMC_GATE_GRAY(element))
13718     {
13719       if (!player->key[EMC_GATE_GRAY_NR(element)])
13720         return MP_NO_ACTION;
13721     }
13722     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13723     {
13724       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13725         return MP_NO_ACTION;
13726     }
13727     else if (element == EL_DC_GATE_WHITE ||
13728              element == EL_DC_GATE_WHITE_GRAY ||
13729              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13730     {
13731       if (player->num_white_keys == 0)
13732         return MP_NO_ACTION;
13733
13734       player->num_white_keys--;
13735     }
13736     else if (IS_SP_PORT(element))
13737     {
13738       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13739           element == EL_SP_GRAVITY_PORT_RIGHT ||
13740           element == EL_SP_GRAVITY_PORT_UP ||
13741           element == EL_SP_GRAVITY_PORT_DOWN)
13742         player->gravity = !player->gravity;
13743       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13744                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13745                element == EL_SP_GRAVITY_ON_PORT_UP ||
13746                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13747         player->gravity = TRUE;
13748       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13749                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13750                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13751                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13752         player->gravity = FALSE;
13753     }
13754
13755     /* automatically move to the next field with double speed */
13756     player->programmed_action = move_direction;
13757
13758     if (player->move_delay_reset_counter == 0)
13759     {
13760       player->move_delay_reset_counter = 2;     /* two double speed steps */
13761
13762       DOUBLE_PLAYER_SPEED(player);
13763     }
13764
13765     PlayLevelSoundAction(x, y, ACTION_PASSING);
13766   }
13767   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13768   {
13769     RemoveField(x, y);
13770
13771     if (mode != DF_SNAP)
13772     {
13773       GfxElement[x][y] = GFX_ELEMENT(element);
13774       player->is_digging = TRUE;
13775     }
13776
13777     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13778
13779     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13780                                         player->index_bit, dig_side);
13781
13782     if (mode == DF_SNAP)
13783     {
13784       if (level.block_snap_field)
13785         setFieldForSnapping(x, y, element, move_direction);
13786       else
13787         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13788
13789       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13790                                           player->index_bit, dig_side);
13791     }
13792   }
13793   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13794   {
13795     RemoveField(x, y);
13796
13797     if (is_player && mode != DF_SNAP)
13798     {
13799       GfxElement[x][y] = element;
13800       player->is_collecting = TRUE;
13801     }
13802
13803     if (element == EL_SPEED_PILL)
13804     {
13805       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13806     }
13807     else if (element == EL_EXTRA_TIME && level.time > 0)
13808     {
13809       TimeLeft += level.extra_time;
13810
13811       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13812
13813       DisplayGameControlValues();
13814     }
13815     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13816     {
13817       player->shield_normal_time_left += level.shield_normal_time;
13818       if (element == EL_SHIELD_DEADLY)
13819         player->shield_deadly_time_left += level.shield_deadly_time;
13820     }
13821     else if (element == EL_DYNAMITE ||
13822              element == EL_EM_DYNAMITE ||
13823              element == EL_SP_DISK_RED)
13824     {
13825       if (player->inventory_size < MAX_INVENTORY_SIZE)
13826         player->inventory_element[player->inventory_size++] = element;
13827
13828       DrawGameDoorValues();
13829     }
13830     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13831     {
13832       player->dynabomb_count++;
13833       player->dynabombs_left++;
13834     }
13835     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13836     {
13837       player->dynabomb_size++;
13838     }
13839     else if (element == EL_DYNABOMB_INCREASE_POWER)
13840     {
13841       player->dynabomb_xl = TRUE;
13842     }
13843     else if (IS_KEY(element))
13844     {
13845       player->key[KEY_NR(element)] = TRUE;
13846
13847       DrawGameDoorValues();
13848     }
13849     else if (element == EL_DC_KEY_WHITE)
13850     {
13851       player->num_white_keys++;
13852
13853       /* display white keys? */
13854       /* DrawGameDoorValues(); */
13855     }
13856     else if (IS_ENVELOPE(element))
13857     {
13858       player->show_envelope = element;
13859     }
13860     else if (element == EL_EMC_LENSES)
13861     {
13862       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13863
13864       RedrawAllInvisibleElementsForLenses();
13865     }
13866     else if (element == EL_EMC_MAGNIFIER)
13867     {
13868       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13869
13870       RedrawAllInvisibleElementsForMagnifier();
13871     }
13872     else if (IS_DROPPABLE(element) ||
13873              IS_THROWABLE(element))     /* can be collected and dropped */
13874     {
13875       int i;
13876
13877       if (collect_count == 0)
13878         player->inventory_infinite_element = element;
13879       else
13880         for (i = 0; i < collect_count; i++)
13881           if (player->inventory_size < MAX_INVENTORY_SIZE)
13882             player->inventory_element[player->inventory_size++] = element;
13883
13884       DrawGameDoorValues();
13885     }
13886     else if (collect_count > 0)
13887     {
13888       local_player->gems_still_needed -= collect_count;
13889       if (local_player->gems_still_needed < 0)
13890         local_player->gems_still_needed = 0;
13891
13892       game.snapshot.collected_item = TRUE;
13893
13894       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13895
13896       DisplayGameControlValues();
13897     }
13898
13899     RaiseScoreElement(element);
13900     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13901
13902     if (is_player)
13903       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13904                                           player->index_bit, dig_side);
13905
13906     if (mode == DF_SNAP)
13907     {
13908       if (level.block_snap_field)
13909         setFieldForSnapping(x, y, element, move_direction);
13910       else
13911         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13912
13913       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13914                                           player->index_bit, dig_side);
13915     }
13916   }
13917   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13918   {
13919     if (mode == DF_SNAP && element != EL_BD_ROCK)
13920       return MP_NO_ACTION;
13921
13922     if (CAN_FALL(element) && dy)
13923       return MP_NO_ACTION;
13924
13925     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13926         !(element == EL_SPRING && level.use_spring_bug))
13927       return MP_NO_ACTION;
13928
13929     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13930         ((move_direction & MV_VERTICAL &&
13931           ((element_info[element].move_pattern & MV_LEFT &&
13932             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13933            (element_info[element].move_pattern & MV_RIGHT &&
13934             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13935          (move_direction & MV_HORIZONTAL &&
13936           ((element_info[element].move_pattern & MV_UP &&
13937             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13938            (element_info[element].move_pattern & MV_DOWN &&
13939             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13940       return MP_NO_ACTION;
13941
13942     /* do not push elements already moving away faster than player */
13943     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13944         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13945       return MP_NO_ACTION;
13946
13947     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13948     {
13949       if (player->push_delay_value == -1 || !player_was_pushing)
13950         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13951     }
13952     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13953     {
13954       if (player->push_delay_value == -1)
13955         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13956     }
13957     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13958     {
13959       if (!player->is_pushing)
13960         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13961     }
13962
13963     player->is_pushing = TRUE;
13964     player->is_active = TRUE;
13965
13966     if (!(IN_LEV_FIELD(nextx, nexty) &&
13967           (IS_FREE(nextx, nexty) ||
13968            (IS_SB_ELEMENT(element) &&
13969             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13970            (IS_CUSTOM_ELEMENT(element) &&
13971             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13972       return MP_NO_ACTION;
13973
13974     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13975       return MP_NO_ACTION;
13976
13977     if (player->push_delay == -1)       /* new pushing; restart delay */
13978       player->push_delay = 0;
13979
13980     if (player->push_delay < player->push_delay_value &&
13981         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13982         element != EL_SPRING && element != EL_BALLOON)
13983     {
13984       /* make sure that there is no move delay before next try to push */
13985       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13986         player->move_delay = 0;
13987
13988       return MP_NO_ACTION;
13989     }
13990
13991     if (IS_CUSTOM_ELEMENT(element) &&
13992         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13993     {
13994       if (!DigFieldByCE(nextx, nexty, element))
13995         return MP_NO_ACTION;
13996     }
13997
13998     if (IS_SB_ELEMENT(element))
13999     {
14000       if (element == EL_SOKOBAN_FIELD_FULL)
14001       {
14002         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14003         local_player->sokobanfields_still_needed++;
14004       }
14005
14006       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14007       {
14008         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14009         local_player->sokobanfields_still_needed--;
14010       }
14011
14012       Feld[x][y] = EL_SOKOBAN_OBJECT;
14013
14014       if (Back[x][y] == Back[nextx][nexty])
14015         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14016       else if (Back[x][y] != 0)
14017         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14018                                     ACTION_EMPTYING);
14019       else
14020         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14021                                     ACTION_FILLING);
14022
14023       if (local_player->sokobanfields_still_needed == 0 &&
14024           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14025       {
14026         PlayerWins(player);
14027
14028         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14029       }
14030     }
14031     else
14032       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14033
14034     InitMovingField(x, y, move_direction);
14035     GfxAction[x][y] = ACTION_PUSHING;
14036
14037     if (mode == DF_SNAP)
14038       ContinueMoving(x, y);
14039     else
14040       MovPos[x][y] = (dx != 0 ? dx : dy);
14041
14042     Pushed[x][y] = TRUE;
14043     Pushed[nextx][nexty] = TRUE;
14044
14045     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14046       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14047     else
14048       player->push_delay_value = -1;    /* get new value later */
14049
14050     /* check for element change _after_ element has been pushed */
14051     if (game.use_change_when_pushing_bug)
14052     {
14053       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14054                                  player->index_bit, dig_side);
14055       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14056                                           player->index_bit, dig_side);
14057     }
14058   }
14059   else if (IS_SWITCHABLE(element))
14060   {
14061     if (PLAYER_SWITCHING(player, x, y))
14062     {
14063       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14064                                           player->index_bit, dig_side);
14065
14066       return MP_ACTION;
14067     }
14068
14069     player->is_switching = TRUE;
14070     player->switch_x = x;
14071     player->switch_y = y;
14072
14073     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14074
14075     if (element == EL_ROBOT_WHEEL)
14076     {
14077       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14078       ZX = x;
14079       ZY = y;
14080
14081       game.robot_wheel_active = TRUE;
14082
14083       TEST_DrawLevelField(x, y);
14084     }
14085     else if (element == EL_SP_TERMINAL)
14086     {
14087       int xx, yy;
14088
14089       SCAN_PLAYFIELD(xx, yy)
14090       {
14091         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14092         {
14093           Bang(xx, yy);
14094         }
14095         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14096         {
14097           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14098
14099           ResetGfxAnimation(xx, yy);
14100           TEST_DrawLevelField(xx, yy);
14101         }
14102       }
14103     }
14104     else if (IS_BELT_SWITCH(element))
14105     {
14106       ToggleBeltSwitch(x, y);
14107     }
14108     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14109              element == EL_SWITCHGATE_SWITCH_DOWN ||
14110              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14111              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14112     {
14113       ToggleSwitchgateSwitch(x, y);
14114     }
14115     else if (element == EL_LIGHT_SWITCH ||
14116              element == EL_LIGHT_SWITCH_ACTIVE)
14117     {
14118       ToggleLightSwitch(x, y);
14119     }
14120     else if (element == EL_TIMEGATE_SWITCH ||
14121              element == EL_DC_TIMEGATE_SWITCH)
14122     {
14123       ActivateTimegateSwitch(x, y);
14124     }
14125     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14126              element == EL_BALLOON_SWITCH_RIGHT ||
14127              element == EL_BALLOON_SWITCH_UP    ||
14128              element == EL_BALLOON_SWITCH_DOWN  ||
14129              element == EL_BALLOON_SWITCH_NONE  ||
14130              element == EL_BALLOON_SWITCH_ANY)
14131     {
14132       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14133                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14134                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14135                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14136                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14137                              move_direction);
14138     }
14139     else if (element == EL_LAMP)
14140     {
14141       Feld[x][y] = EL_LAMP_ACTIVE;
14142       local_player->lights_still_needed--;
14143
14144       ResetGfxAnimation(x, y);
14145       TEST_DrawLevelField(x, y);
14146     }
14147     else if (element == EL_TIME_ORB_FULL)
14148     {
14149       Feld[x][y] = EL_TIME_ORB_EMPTY;
14150
14151       if (level.time > 0 || level.use_time_orb_bug)
14152       {
14153         TimeLeft += level.time_orb_time;
14154         game.no_time_limit = FALSE;
14155
14156         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14157
14158         DisplayGameControlValues();
14159       }
14160
14161       ResetGfxAnimation(x, y);
14162       TEST_DrawLevelField(x, y);
14163     }
14164     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14165              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14166     {
14167       int xx, yy;
14168
14169       game.ball_state = !game.ball_state;
14170
14171       SCAN_PLAYFIELD(xx, yy)
14172       {
14173         int e = Feld[xx][yy];
14174
14175         if (game.ball_state)
14176         {
14177           if (e == EL_EMC_MAGIC_BALL)
14178             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14179           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14180             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14181         }
14182         else
14183         {
14184           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14185             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14186           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14187             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14188         }
14189       }
14190     }
14191
14192     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14193                                         player->index_bit, dig_side);
14194
14195     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14196                                         player->index_bit, dig_side);
14197
14198     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14199                                         player->index_bit, dig_side);
14200
14201     return MP_ACTION;
14202   }
14203   else
14204   {
14205     if (!PLAYER_SWITCHING(player, x, y))
14206     {
14207       player->is_switching = TRUE;
14208       player->switch_x = x;
14209       player->switch_y = y;
14210
14211       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14212                                  player->index_bit, dig_side);
14213       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14214                                           player->index_bit, dig_side);
14215
14216       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14217                                  player->index_bit, dig_side);
14218       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14219                                           player->index_bit, dig_side);
14220     }
14221
14222     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14223                                player->index_bit, dig_side);
14224     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14225                                         player->index_bit, dig_side);
14226
14227     return MP_NO_ACTION;
14228   }
14229
14230   player->push_delay = -1;
14231
14232   if (is_player)                /* function can also be called by EL_PENGUIN */
14233   {
14234     if (Feld[x][y] != element)          /* really digged/collected something */
14235     {
14236       player->is_collecting = !player->is_digging;
14237       player->is_active = TRUE;
14238     }
14239   }
14240
14241   return MP_MOVING;
14242 }
14243
14244 static boolean DigFieldByCE(int x, int y, int digging_element)
14245 {
14246   int element = Feld[x][y];
14247
14248   if (!IS_FREE(x, y))
14249   {
14250     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14251                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14252                   ACTION_BREAKING);
14253
14254     /* no element can dig solid indestructible elements */
14255     if (IS_INDESTRUCTIBLE(element) &&
14256         !IS_DIGGABLE(element) &&
14257         !IS_COLLECTIBLE(element))
14258       return FALSE;
14259
14260     if (AmoebaNr[x][y] &&
14261         (element == EL_AMOEBA_FULL ||
14262          element == EL_BD_AMOEBA ||
14263          element == EL_AMOEBA_GROWING))
14264     {
14265       AmoebaCnt[AmoebaNr[x][y]]--;
14266       AmoebaCnt2[AmoebaNr[x][y]]--;
14267     }
14268
14269     if (IS_MOVING(x, y))
14270       RemoveMovingField(x, y);
14271     else
14272     {
14273       RemoveField(x, y);
14274       TEST_DrawLevelField(x, y);
14275     }
14276
14277     /* if digged element was about to explode, prevent the explosion */
14278     ExplodeField[x][y] = EX_TYPE_NONE;
14279
14280     PlayLevelSoundAction(x, y, action);
14281   }
14282
14283   Store[x][y] = EL_EMPTY;
14284
14285   /* this makes it possible to leave the removed element again */
14286   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14287     Store[x][y] = element;
14288
14289   return TRUE;
14290 }
14291
14292 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14293 {
14294   int jx = player->jx, jy = player->jy;
14295   int x = jx + dx, y = jy + dy;
14296   int snap_direction = (dx == -1 ? MV_LEFT  :
14297                         dx == +1 ? MV_RIGHT :
14298                         dy == -1 ? MV_UP    :
14299                         dy == +1 ? MV_DOWN  : MV_NONE);
14300   boolean can_continue_snapping = (level.continuous_snapping &&
14301                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14302
14303   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14304     return FALSE;
14305
14306   if (!player->active || !IN_LEV_FIELD(x, y))
14307     return FALSE;
14308
14309   if (dx && dy)
14310     return FALSE;
14311
14312   if (!dx && !dy)
14313   {
14314     if (player->MovPos == 0)
14315       player->is_pushing = FALSE;
14316
14317     player->is_snapping = FALSE;
14318
14319     if (player->MovPos == 0)
14320     {
14321       player->is_moving = FALSE;
14322       player->is_digging = FALSE;
14323       player->is_collecting = FALSE;
14324     }
14325
14326     return FALSE;
14327   }
14328
14329   /* prevent snapping with already pressed snap key when not allowed */
14330   if (player->is_snapping && !can_continue_snapping)
14331     return FALSE;
14332
14333   player->MovDir = snap_direction;
14334
14335   if (player->MovPos == 0)
14336   {
14337     player->is_moving = FALSE;
14338     player->is_digging = FALSE;
14339     player->is_collecting = FALSE;
14340   }
14341
14342   player->is_dropping = FALSE;
14343   player->is_dropping_pressed = FALSE;
14344   player->drop_pressed_delay = 0;
14345
14346   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14347     return FALSE;
14348
14349   player->is_snapping = TRUE;
14350   player->is_active = TRUE;
14351
14352   if (player->MovPos == 0)
14353   {
14354     player->is_moving = FALSE;
14355     player->is_digging = FALSE;
14356     player->is_collecting = FALSE;
14357   }
14358
14359   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14360     TEST_DrawLevelField(player->last_jx, player->last_jy);
14361
14362   TEST_DrawLevelField(x, y);
14363
14364   return TRUE;
14365 }
14366
14367 static boolean DropElement(struct PlayerInfo *player)
14368 {
14369   int old_element, new_element;
14370   int dropx = player->jx, dropy = player->jy;
14371   int drop_direction = player->MovDir;
14372   int drop_side = drop_direction;
14373   int drop_element = get_next_dropped_element(player);
14374
14375   /* do not drop an element on top of another element; when holding drop key
14376      pressed without moving, dropped element must move away before the next
14377      element can be dropped (this is especially important if the next element
14378      is dynamite, which can be placed on background for historical reasons) */
14379   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14380     return MP_ACTION;
14381
14382   if (IS_THROWABLE(drop_element))
14383   {
14384     dropx += GET_DX_FROM_DIR(drop_direction);
14385     dropy += GET_DY_FROM_DIR(drop_direction);
14386
14387     if (!IN_LEV_FIELD(dropx, dropy))
14388       return FALSE;
14389   }
14390
14391   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14392   new_element = drop_element;           /* default: no change when dropping */
14393
14394   /* check if player is active, not moving and ready to drop */
14395   if (!player->active || player->MovPos || player->drop_delay > 0)
14396     return FALSE;
14397
14398   /* check if player has anything that can be dropped */
14399   if (new_element == EL_UNDEFINED)
14400     return FALSE;
14401
14402   /* only set if player has anything that can be dropped */
14403   player->is_dropping_pressed = TRUE;
14404
14405   /* check if drop key was pressed long enough for EM style dynamite */
14406   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14407     return FALSE;
14408
14409   /* check if anything can be dropped at the current position */
14410   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14411     return FALSE;
14412
14413   /* collected custom elements can only be dropped on empty fields */
14414   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14415     return FALSE;
14416
14417   if (old_element != EL_EMPTY)
14418     Back[dropx][dropy] = old_element;   /* store old element on this field */
14419
14420   ResetGfxAnimation(dropx, dropy);
14421   ResetRandomAnimationValue(dropx, dropy);
14422
14423   if (player->inventory_size > 0 ||
14424       player->inventory_infinite_element != EL_UNDEFINED)
14425   {
14426     if (player->inventory_size > 0)
14427     {
14428       player->inventory_size--;
14429
14430       DrawGameDoorValues();
14431
14432       if (new_element == EL_DYNAMITE)
14433         new_element = EL_DYNAMITE_ACTIVE;
14434       else if (new_element == EL_EM_DYNAMITE)
14435         new_element = EL_EM_DYNAMITE_ACTIVE;
14436       else if (new_element == EL_SP_DISK_RED)
14437         new_element = EL_SP_DISK_RED_ACTIVE;
14438     }
14439
14440     Feld[dropx][dropy] = new_element;
14441
14442     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14443       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14444                           el2img(Feld[dropx][dropy]), 0);
14445
14446     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14447
14448     /* needed if previous element just changed to "empty" in the last frame */
14449     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14450
14451     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14452                                player->index_bit, drop_side);
14453     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14454                                         CE_PLAYER_DROPS_X,
14455                                         player->index_bit, drop_side);
14456
14457     TestIfElementTouchesCustomElement(dropx, dropy);
14458   }
14459   else          /* player is dropping a dyna bomb */
14460   {
14461     player->dynabombs_left--;
14462
14463     Feld[dropx][dropy] = new_element;
14464
14465     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14466       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14467                           el2img(Feld[dropx][dropy]), 0);
14468
14469     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14470   }
14471
14472   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14473     InitField_WithBug1(dropx, dropy, FALSE);
14474
14475   new_element = Feld[dropx][dropy];     /* element might have changed */
14476
14477   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14478       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14479   {
14480     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14481       MovDir[dropx][dropy] = drop_direction;
14482
14483     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14484
14485     /* do not cause impact style collision by dropping elements that can fall */
14486     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14487   }
14488
14489   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14490   player->is_dropping = TRUE;
14491
14492   player->drop_pressed_delay = 0;
14493   player->is_dropping_pressed = FALSE;
14494
14495   player->drop_x = dropx;
14496   player->drop_y = dropy;
14497
14498   return TRUE;
14499 }
14500
14501 /* ------------------------------------------------------------------------- */
14502 /* game sound playing functions                                              */
14503 /* ------------------------------------------------------------------------- */
14504
14505 static int *loop_sound_frame = NULL;
14506 static int *loop_sound_volume = NULL;
14507
14508 void InitPlayLevelSound()
14509 {
14510   int num_sounds = getSoundListSize();
14511
14512   checked_free(loop_sound_frame);
14513   checked_free(loop_sound_volume);
14514
14515   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14516   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14517 }
14518
14519 static void PlayLevelSound(int x, int y, int nr)
14520 {
14521   int sx = SCREENX(x), sy = SCREENY(y);
14522   int volume, stereo_position;
14523   int max_distance = 8;
14524   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14525
14526   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14527       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14528     return;
14529
14530   if (!IN_LEV_FIELD(x, y) ||
14531       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14532       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14533     return;
14534
14535   volume = SOUND_MAX_VOLUME;
14536
14537   if (!IN_SCR_FIELD(sx, sy))
14538   {
14539     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14540     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14541
14542     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14543   }
14544
14545   stereo_position = (SOUND_MAX_LEFT +
14546                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14547                      (SCR_FIELDX + 2 * max_distance));
14548
14549   if (IS_LOOP_SOUND(nr))
14550   {
14551     /* This assures that quieter loop sounds do not overwrite louder ones,
14552        while restarting sound volume comparison with each new game frame. */
14553
14554     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14555       return;
14556
14557     loop_sound_volume[nr] = volume;
14558     loop_sound_frame[nr] = FrameCounter;
14559   }
14560
14561   PlaySoundExt(nr, volume, stereo_position, type);
14562 }
14563
14564 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14565 {
14566   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14567                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14568                  y < LEVELY(BY1) ? LEVELY(BY1) :
14569                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14570                  sound_action);
14571 }
14572
14573 static void PlayLevelSoundAction(int x, int y, int action)
14574 {
14575   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14576 }
14577
14578 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14579 {
14580   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14581
14582   if (sound_effect != SND_UNDEFINED)
14583     PlayLevelSound(x, y, sound_effect);
14584 }
14585
14586 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14587                                               int action)
14588 {
14589   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14590
14591   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14592     PlayLevelSound(x, y, sound_effect);
14593 }
14594
14595 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14596 {
14597   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14598
14599   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14600     PlayLevelSound(x, y, sound_effect);
14601 }
14602
14603 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14604 {
14605   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14606
14607   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14608     StopSound(sound_effect);
14609 }
14610
14611 static int getLevelMusicNr()
14612 {
14613   if (levelset.music[level_nr] != MUS_UNDEFINED)
14614     return levelset.music[level_nr];            /* from config file */
14615   else
14616     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14617 }
14618
14619 static void FadeLevelSounds()
14620 {
14621   FadeSounds();
14622 }
14623
14624 static void FadeLevelMusic()
14625 {
14626   int music_nr = getLevelMusicNr();
14627   char *curr_music = getCurrentlyPlayingMusicFilename();
14628   char *next_music = getMusicInfoEntryFilename(music_nr);
14629
14630   if (!strEqual(curr_music, next_music))
14631     FadeMusic();
14632 }
14633
14634 void FadeLevelSoundsAndMusic()
14635 {
14636   FadeLevelSounds();
14637   FadeLevelMusic();
14638 }
14639
14640 static void PlayLevelMusic()
14641 {
14642   int music_nr = getLevelMusicNr();
14643   char *curr_music = getCurrentlyPlayingMusicFilename();
14644   char *next_music = getMusicInfoEntryFilename(music_nr);
14645
14646   if (!strEqual(curr_music, next_music))
14647     PlayMusic(music_nr);
14648 }
14649
14650 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14651 {
14652   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14653   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14654   int x = xx - 1 - offset;
14655   int y = yy - 1 - offset;
14656
14657   switch (sample)
14658   {
14659     case SAMPLE_blank:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14661       break;
14662
14663     case SAMPLE_roll:
14664       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14665       break;
14666
14667     case SAMPLE_stone:
14668       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14669       break;
14670
14671     case SAMPLE_nut:
14672       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14673       break;
14674
14675     case SAMPLE_crack:
14676       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14677       break;
14678
14679     case SAMPLE_bug:
14680       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14681       break;
14682
14683     case SAMPLE_tank:
14684       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14685       break;
14686
14687     case SAMPLE_android_clone:
14688       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14689       break;
14690
14691     case SAMPLE_android_move:
14692       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14693       break;
14694
14695     case SAMPLE_spring:
14696       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14697       break;
14698
14699     case SAMPLE_slurp:
14700       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14701       break;
14702
14703     case SAMPLE_eater:
14704       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14705       break;
14706
14707     case SAMPLE_eater_eat:
14708       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14709       break;
14710
14711     case SAMPLE_alien:
14712       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14713       break;
14714
14715     case SAMPLE_collect:
14716       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14717       break;
14718
14719     case SAMPLE_diamond:
14720       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14721       break;
14722
14723     case SAMPLE_squash:
14724       /* !!! CHECK THIS !!! */
14725 #if 1
14726       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14727 #else
14728       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14729 #endif
14730       break;
14731
14732     case SAMPLE_wonderfall:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14734       break;
14735
14736     case SAMPLE_drip:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14738       break;
14739
14740     case SAMPLE_push:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14742       break;
14743
14744     case SAMPLE_dirt:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14746       break;
14747
14748     case SAMPLE_acid:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14750       break;
14751
14752     case SAMPLE_ball:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14754       break;
14755
14756     case SAMPLE_grow:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14758       break;
14759
14760     case SAMPLE_wonder:
14761       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14762       break;
14763
14764     case SAMPLE_door:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14766       break;
14767
14768     case SAMPLE_exit_open:
14769       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14770       break;
14771
14772     case SAMPLE_exit_leave:
14773       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14774       break;
14775
14776     case SAMPLE_dynamite:
14777       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14778       break;
14779
14780     case SAMPLE_tick:
14781       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14782       break;
14783
14784     case SAMPLE_press:
14785       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14786       break;
14787
14788     case SAMPLE_wheel:
14789       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14790       break;
14791
14792     case SAMPLE_boom:
14793       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14794       break;
14795
14796     case SAMPLE_die:
14797       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14798       break;
14799
14800     case SAMPLE_time:
14801       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14802       break;
14803
14804     default:
14805       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14806       break;
14807   }
14808 }
14809
14810 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14811 {
14812   int element = map_element_SP_to_RND(element_sp);
14813   int action = map_action_SP_to_RND(action_sp);
14814   int offset = (setup.sp_show_border_elements ? 0 : 1);
14815   int x = xx - offset;
14816   int y = yy - offset;
14817
14818   PlayLevelSoundElementAction(x, y, element, action);
14819 }
14820
14821 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14822 {
14823   int element = map_element_MM_to_RND(element_mm);
14824   int action = map_action_MM_to_RND(action_mm);
14825   int offset = 0;
14826   int x = xx - offset;
14827   int y = yy - offset;
14828
14829   if (!IS_MM_ELEMENT(element))
14830     element = EL_MM_DEFAULT;
14831
14832   PlayLevelSoundElementAction(x, y, element, action);
14833 }
14834
14835 void PlaySound_MM(int sound_mm)
14836 {
14837   int sound = map_sound_MM_to_RND(sound_mm);
14838
14839   if (sound == SND_UNDEFINED)
14840     return;
14841
14842   PlaySound(sound);
14843 }
14844
14845 void PlaySoundLoop_MM(int sound_mm)
14846 {
14847   int sound = map_sound_MM_to_RND(sound_mm);
14848
14849   if (sound == SND_UNDEFINED)
14850     return;
14851
14852   PlaySoundLoop(sound);
14853 }
14854
14855 void StopSound_MM(int sound_mm)
14856 {
14857   int sound = map_sound_MM_to_RND(sound_mm);
14858
14859   if (sound == SND_UNDEFINED)
14860     return;
14861
14862   StopSound(sound);
14863 }
14864
14865 void RaiseScore(int value)
14866 {
14867   local_player->score += value;
14868
14869   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14870
14871   DisplayGameControlValues();
14872 }
14873
14874 void RaiseScoreElement(int element)
14875 {
14876   switch (element)
14877   {
14878     case EL_EMERALD:
14879     case EL_BD_DIAMOND:
14880     case EL_EMERALD_YELLOW:
14881     case EL_EMERALD_RED:
14882     case EL_EMERALD_PURPLE:
14883     case EL_SP_INFOTRON:
14884       RaiseScore(level.score[SC_EMERALD]);
14885       break;
14886     case EL_DIAMOND:
14887       RaiseScore(level.score[SC_DIAMOND]);
14888       break;
14889     case EL_CRYSTAL:
14890       RaiseScore(level.score[SC_CRYSTAL]);
14891       break;
14892     case EL_PEARL:
14893       RaiseScore(level.score[SC_PEARL]);
14894       break;
14895     case EL_BUG:
14896     case EL_BD_BUTTERFLY:
14897     case EL_SP_ELECTRON:
14898       RaiseScore(level.score[SC_BUG]);
14899       break;
14900     case EL_SPACESHIP:
14901     case EL_BD_FIREFLY:
14902     case EL_SP_SNIKSNAK:
14903       RaiseScore(level.score[SC_SPACESHIP]);
14904       break;
14905     case EL_YAMYAM:
14906     case EL_DARK_YAMYAM:
14907       RaiseScore(level.score[SC_YAMYAM]);
14908       break;
14909     case EL_ROBOT:
14910       RaiseScore(level.score[SC_ROBOT]);
14911       break;
14912     case EL_PACMAN:
14913       RaiseScore(level.score[SC_PACMAN]);
14914       break;
14915     case EL_NUT:
14916       RaiseScore(level.score[SC_NUT]);
14917       break;
14918     case EL_DYNAMITE:
14919     case EL_EM_DYNAMITE:
14920     case EL_SP_DISK_RED:
14921     case EL_DYNABOMB_INCREASE_NUMBER:
14922     case EL_DYNABOMB_INCREASE_SIZE:
14923     case EL_DYNABOMB_INCREASE_POWER:
14924       RaiseScore(level.score[SC_DYNAMITE]);
14925       break;
14926     case EL_SHIELD_NORMAL:
14927     case EL_SHIELD_DEADLY:
14928       RaiseScore(level.score[SC_SHIELD]);
14929       break;
14930     case EL_EXTRA_TIME:
14931       RaiseScore(level.extra_time_score);
14932       break;
14933     case EL_KEY_1:
14934     case EL_KEY_2:
14935     case EL_KEY_3:
14936     case EL_KEY_4:
14937     case EL_EM_KEY_1:
14938     case EL_EM_KEY_2:
14939     case EL_EM_KEY_3:
14940     case EL_EM_KEY_4:
14941     case EL_EMC_KEY_5:
14942     case EL_EMC_KEY_6:
14943     case EL_EMC_KEY_7:
14944     case EL_EMC_KEY_8:
14945     case EL_DC_KEY_WHITE:
14946       RaiseScore(level.score[SC_KEY]);
14947       break;
14948     default:
14949       RaiseScore(element_info[element].collect_score);
14950       break;
14951   }
14952 }
14953
14954 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14955 {
14956   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14957   {
14958     /* closing door required in case of envelope style request dialogs */
14959     if (!skip_request)
14960       CloseDoor(DOOR_CLOSE_1);
14961
14962     if (network.enabled)
14963       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14964     else
14965     {
14966       if (quick_quit)
14967         FadeSkipNextFadeIn();
14968
14969       SetGameStatus(GAME_MODE_MAIN);
14970
14971       DrawMainMenu();
14972     }
14973   }
14974   else          /* continue playing the game */
14975   {
14976     if (tape.playing && tape.deactivate_display)
14977       TapeDeactivateDisplayOff(TRUE);
14978
14979     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14980
14981     if (tape.playing && tape.deactivate_display)
14982       TapeDeactivateDisplayOn();
14983   }
14984 }
14985
14986 void RequestQuitGame(boolean ask_if_really_quit)
14987 {
14988   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14989   boolean skip_request = AllPlayersGone || quick_quit;
14990
14991   RequestQuitGameExt(skip_request, quick_quit,
14992                      "Do you really want to quit the game?");
14993 }
14994
14995 void RequestRestartGame(char *message)
14996 {
14997   game.restart_game_message = NULL;
14998
14999   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
15000   {
15001     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15002   }
15003   else
15004   {
15005     SetGameStatus(GAME_MODE_MAIN);
15006
15007     DrawMainMenu();
15008   }
15009 }
15010
15011
15012 /* ------------------------------------------------------------------------- */
15013 /* random generator functions                                                */
15014 /* ------------------------------------------------------------------------- */
15015
15016 unsigned int InitEngineRandom_RND(int seed)
15017 {
15018   game.num_random_calls = 0;
15019
15020   return InitEngineRandom(seed);
15021 }
15022
15023 unsigned int RND(int max)
15024 {
15025   if (max > 0)
15026   {
15027     game.num_random_calls++;
15028
15029     return GetEngineRandom(max);
15030   }
15031
15032   return 0;
15033 }
15034
15035
15036 /* ------------------------------------------------------------------------- */
15037 /* game engine snapshot handling functions                                   */
15038 /* ------------------------------------------------------------------------- */
15039
15040 struct EngineSnapshotInfo
15041 {
15042   /* runtime values for custom element collect score */
15043   int collect_score[NUM_CUSTOM_ELEMENTS];
15044
15045   /* runtime values for group element choice position */
15046   int choice_pos[NUM_GROUP_ELEMENTS];
15047
15048   /* runtime values for belt position animations */
15049   int belt_graphic[4][NUM_BELT_PARTS];
15050   int belt_anim_mode[4][NUM_BELT_PARTS];
15051 };
15052
15053 static struct EngineSnapshotInfo engine_snapshot_rnd;
15054 static char *snapshot_level_identifier = NULL;
15055 static int snapshot_level_nr = -1;
15056
15057 static void SaveEngineSnapshotValues_RND()
15058 {
15059   static int belt_base_active_element[4] =
15060   {
15061     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15062     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15063     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15064     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15065   };
15066   int i, j;
15067
15068   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15069   {
15070     int element = EL_CUSTOM_START + i;
15071
15072     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15073   }
15074
15075   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15076   {
15077     int element = EL_GROUP_START + i;
15078
15079     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15080   }
15081
15082   for (i = 0; i < 4; i++)
15083   {
15084     for (j = 0; j < NUM_BELT_PARTS; j++)
15085     {
15086       int element = belt_base_active_element[i] + j;
15087       int graphic = el2img(element);
15088       int anim_mode = graphic_info[graphic].anim_mode;
15089
15090       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15091       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15092     }
15093   }
15094 }
15095
15096 static void LoadEngineSnapshotValues_RND()
15097 {
15098   unsigned int num_random_calls = game.num_random_calls;
15099   int i, j;
15100
15101   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15102   {
15103     int element = EL_CUSTOM_START + i;
15104
15105     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15106   }
15107
15108   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15109   {
15110     int element = EL_GROUP_START + i;
15111
15112     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15113   }
15114
15115   for (i = 0; i < 4; i++)
15116   {
15117     for (j = 0; j < NUM_BELT_PARTS; j++)
15118     {
15119       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15120       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15121
15122       graphic_info[graphic].anim_mode = anim_mode;
15123     }
15124   }
15125
15126   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15127   {
15128     InitRND(tape.random_seed);
15129     for (i = 0; i < num_random_calls; i++)
15130       RND(1);
15131   }
15132
15133   if (game.num_random_calls != num_random_calls)
15134   {
15135     Error(ERR_INFO, "number of random calls out of sync");
15136     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15137     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15138     Error(ERR_EXIT, "this should not happen -- please debug");
15139   }
15140 }
15141
15142 void FreeEngineSnapshotSingle()
15143 {
15144   FreeSnapshotSingle();
15145
15146   setString(&snapshot_level_identifier, NULL);
15147   snapshot_level_nr = -1;
15148 }
15149
15150 void FreeEngineSnapshotList()
15151 {
15152   FreeSnapshotList();
15153 }
15154
15155 ListNode *SaveEngineSnapshotBuffers()
15156 {
15157   ListNode *buffers = NULL;
15158
15159   /* copy some special values to a structure better suited for the snapshot */
15160
15161   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15162     SaveEngineSnapshotValues_RND();
15163   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15164     SaveEngineSnapshotValues_EM();
15165   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15166     SaveEngineSnapshotValues_SP(&buffers);
15167   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15168     SaveEngineSnapshotValues_MM(&buffers);
15169
15170   /* save values stored in special snapshot structure */
15171
15172   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15173     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15174   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15175     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15176   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15177     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15178   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15179     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15180
15181   /* save further RND engine values */
15182
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15186
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15191
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15197
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15201
15202   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15203
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15205
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15207   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15208
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15213   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15216   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15218   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15219   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15220   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15221   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15222   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15223   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15224   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15225   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15226   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15227
15228   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15229   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15230
15231   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15232   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15233   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15234
15235   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15236   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15237
15238   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15239   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15240   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15241   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15242   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15243
15244   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15246
15247 #if 0
15248   ListNode *node = engine_snapshot_list_rnd;
15249   int num_bytes = 0;
15250
15251   while (node != NULL)
15252   {
15253     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15254
15255     node = node->next;
15256   }
15257
15258   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15259 #endif
15260
15261   return buffers;
15262 }
15263
15264 void SaveEngineSnapshotSingle()
15265 {
15266   ListNode *buffers = SaveEngineSnapshotBuffers();
15267
15268   /* finally save all snapshot buffers to single snapshot */
15269   SaveSnapshotSingle(buffers);
15270
15271   /* save level identification information */
15272   setString(&snapshot_level_identifier, leveldir_current->identifier);
15273   snapshot_level_nr = level_nr;
15274 }
15275
15276 boolean CheckSaveEngineSnapshotToList()
15277 {
15278   boolean save_snapshot =
15279     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15280      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15281       game.snapshot.changed_action) ||
15282      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15283       game.snapshot.collected_item));
15284
15285   game.snapshot.changed_action = FALSE;
15286   game.snapshot.collected_item = FALSE;
15287   game.snapshot.save_snapshot = save_snapshot;
15288
15289   return save_snapshot;
15290 }
15291
15292 void SaveEngineSnapshotToList()
15293 {
15294   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15295       tape.quick_resume)
15296     return;
15297
15298   ListNode *buffers = SaveEngineSnapshotBuffers();
15299
15300   /* finally save all snapshot buffers to snapshot list */
15301   SaveSnapshotToList(buffers);
15302 }
15303
15304 void SaveEngineSnapshotToListInitial()
15305 {
15306   FreeEngineSnapshotList();
15307
15308   SaveEngineSnapshotToList();
15309 }
15310
15311 void LoadEngineSnapshotValues()
15312 {
15313   /* restore special values from snapshot structure */
15314
15315   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15316     LoadEngineSnapshotValues_RND();
15317   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15318     LoadEngineSnapshotValues_EM();
15319   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15320     LoadEngineSnapshotValues_SP();
15321   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15322     LoadEngineSnapshotValues_MM();
15323 }
15324
15325 void LoadEngineSnapshotSingle()
15326 {
15327   LoadSnapshotSingle();
15328
15329   LoadEngineSnapshotValues();
15330 }
15331
15332 void LoadEngineSnapshot_Undo(int steps)
15333 {
15334   LoadSnapshotFromList_Older(steps);
15335
15336   LoadEngineSnapshotValues();
15337 }
15338
15339 void LoadEngineSnapshot_Redo(int steps)
15340 {
15341   LoadSnapshotFromList_Newer(steps);
15342
15343   LoadEngineSnapshotValues();
15344 }
15345
15346 boolean CheckEngineSnapshotSingle()
15347 {
15348   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15349           snapshot_level_nr == level_nr);
15350 }
15351
15352 boolean CheckEngineSnapshotList()
15353 {
15354   return CheckSnapshotList();
15355 }
15356
15357
15358 /* ---------- new game button stuff ---------------------------------------- */
15359
15360 static struct
15361 {
15362   int graphic;
15363   struct XY *pos;
15364   int gadget_id;
15365   boolean *setup_value;
15366   boolean allowed_on_tape;
15367   char *infotext;
15368 } gamebutton_info[NUM_GAME_BUTTONS] =
15369 {
15370   {
15371     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15372     GAME_CTRL_ID_STOP,                          NULL,
15373     TRUE,                                       "stop game"
15374   },
15375   {
15376     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15377     GAME_CTRL_ID_PAUSE,                         NULL,
15378     TRUE,                                       "pause game"
15379   },
15380   {
15381     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15382     GAME_CTRL_ID_PLAY,                          NULL,
15383     TRUE,                                       "play game"
15384   },
15385   {
15386     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15387     GAME_CTRL_ID_UNDO,                          NULL,
15388     TRUE,                                       "undo step"
15389   },
15390   {
15391     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15392     GAME_CTRL_ID_REDO,                          NULL,
15393     TRUE,                                       "redo step"
15394   },
15395   {
15396     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15397     GAME_CTRL_ID_SAVE,                          NULL,
15398     TRUE,                                       "save game"
15399   },
15400   {
15401     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15402     GAME_CTRL_ID_PAUSE2,                        NULL,
15403     TRUE,                                       "pause game"
15404   },
15405   {
15406     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15407     GAME_CTRL_ID_LOAD,                          NULL,
15408     TRUE,                                       "load game"
15409   },
15410   {
15411     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15412     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15413     FALSE,                                      "stop game"
15414   },
15415   {
15416     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15417     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15418     FALSE,                                      "pause game"
15419   },
15420   {
15421     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15422     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15423     FALSE,                                      "play game"
15424   },
15425   {
15426     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15427     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15428     TRUE,                                       "background music on/off"
15429   },
15430   {
15431     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15432     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15433     TRUE,                                       "sound loops on/off"
15434   },
15435   {
15436     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15437     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15438     TRUE,                                       "normal sounds on/off"
15439   },
15440   {
15441     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15442     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15443     FALSE,                                      "background music on/off"
15444   },
15445   {
15446     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15447     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15448     FALSE,                                      "sound loops on/off"
15449   },
15450   {
15451     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15452     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15453     FALSE,                                      "normal sounds on/off"
15454   }
15455 };
15456
15457 void CreateGameButtons()
15458 {
15459   int i;
15460
15461   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15462   {
15463     int graphic = gamebutton_info[i].graphic;
15464     struct GraphicInfo *gfx = &graphic_info[graphic];
15465     struct XY *pos = gamebutton_info[i].pos;
15466     struct GadgetInfo *gi;
15467     int button_type;
15468     boolean checked;
15469     unsigned int event_mask;
15470     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15471     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15472     int base_x = (on_tape ? VX : DX);
15473     int base_y = (on_tape ? VY : DY);
15474     int gd_x   = gfx->src_x;
15475     int gd_y   = gfx->src_y;
15476     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15477     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15478     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15479     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15480     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15481     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15482     int id = i;
15483
15484     if (gfx->bitmap == NULL)
15485     {
15486       game_gadget[id] = NULL;
15487
15488       continue;
15489     }
15490
15491     if (id == GAME_CTRL_ID_STOP ||
15492         id == GAME_CTRL_ID_PANEL_STOP ||
15493         id == GAME_CTRL_ID_PLAY ||
15494         id == GAME_CTRL_ID_PANEL_PLAY ||
15495         id == GAME_CTRL_ID_SAVE ||
15496         id == GAME_CTRL_ID_LOAD)
15497     {
15498       button_type = GD_TYPE_NORMAL_BUTTON;
15499       checked = FALSE;
15500       event_mask = GD_EVENT_RELEASED;
15501     }
15502     else if (id == GAME_CTRL_ID_UNDO ||
15503              id == GAME_CTRL_ID_REDO)
15504     {
15505       button_type = GD_TYPE_NORMAL_BUTTON;
15506       checked = FALSE;
15507       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15508     }
15509     else
15510     {
15511       button_type = GD_TYPE_CHECK_BUTTON;
15512       checked = (gamebutton_info[i].setup_value != NULL ?
15513                  *gamebutton_info[i].setup_value : FALSE);
15514       event_mask = GD_EVENT_PRESSED;
15515     }
15516
15517     gi = CreateGadget(GDI_CUSTOM_ID, id,
15518                       GDI_IMAGE_ID, graphic,
15519                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15520                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15521                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15522                       GDI_WIDTH, gfx->width,
15523                       GDI_HEIGHT, gfx->height,
15524                       GDI_TYPE, button_type,
15525                       GDI_STATE, GD_BUTTON_UNPRESSED,
15526                       GDI_CHECKED, checked,
15527                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15528                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15529                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15530                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15531                       GDI_DIRECT_DRAW, FALSE,
15532                       GDI_EVENT_MASK, event_mask,
15533                       GDI_CALLBACK_ACTION, HandleGameButtons,
15534                       GDI_END);
15535
15536     if (gi == NULL)
15537       Error(ERR_EXIT, "cannot create gadget");
15538
15539     game_gadget[id] = gi;
15540   }
15541 }
15542
15543 void FreeGameButtons()
15544 {
15545   int i;
15546
15547   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15548     FreeGadget(game_gadget[i]);
15549 }
15550
15551 static void UnmapGameButtonsAtSamePosition(int id)
15552 {
15553   int i;
15554
15555   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15556     if (i != id &&
15557         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15558         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15559       UnmapGadget(game_gadget[i]);
15560 }
15561
15562 static void UnmapGameButtonsAtSamePosition_All()
15563 {
15564   if (setup.show_snapshot_buttons)
15565   {
15566     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15567     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15568     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15569   }
15570   else
15571   {
15572     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15573     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15574     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15575
15576     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15577     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15578     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15579   }
15580 }
15581
15582 static void MapGameButtonsAtSamePosition(int id)
15583 {
15584   int i;
15585
15586   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15587     if (i != id &&
15588         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15589         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15590       MapGadget(game_gadget[i]);
15591
15592   UnmapGameButtonsAtSamePosition_All();
15593 }
15594
15595 void MapUndoRedoButtons()
15596 {
15597   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15598   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15599
15600   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15601   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15602
15603   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15604 }
15605
15606 void UnmapUndoRedoButtons()
15607 {
15608   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15609   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15610
15611   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15612   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15613
15614   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15615 }
15616
15617 void MapGameButtonsExt(boolean on_tape)
15618 {
15619   int i;
15620
15621   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15622     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15623         i != GAME_CTRL_ID_UNDO &&
15624         i != GAME_CTRL_ID_REDO)
15625       MapGadget(game_gadget[i]);
15626
15627   UnmapGameButtonsAtSamePosition_All();
15628
15629   RedrawGameButtons();
15630 }
15631
15632 void UnmapGameButtonsExt(boolean on_tape)
15633 {
15634   int i;
15635
15636   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15637     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15638       UnmapGadget(game_gadget[i]);
15639 }
15640
15641 void RedrawGameButtonsExt(boolean on_tape)
15642 {
15643   int i;
15644
15645   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15646     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15647       RedrawGadget(game_gadget[i]);
15648
15649   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15650   redraw_mask &= ~REDRAW_ALL;
15651 }
15652
15653 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15654 {
15655   if (gi == NULL)
15656     return;
15657
15658   gi->checked = state;
15659 }
15660
15661 void RedrawSoundButtonGadget(int id)
15662 {
15663   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15664              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15665              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15666              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15667              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15668              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15669              id);
15670
15671   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15672   RedrawGadget(game_gadget[id2]);
15673 }
15674
15675 void MapGameButtons()
15676 {
15677   MapGameButtonsExt(FALSE);
15678 }
15679
15680 void UnmapGameButtons()
15681 {
15682   UnmapGameButtonsExt(FALSE);
15683 }
15684
15685 void RedrawGameButtons()
15686 {
15687   RedrawGameButtonsExt(FALSE);
15688 }
15689
15690 void MapGameButtonsOnTape()
15691 {
15692   MapGameButtonsExt(TRUE);
15693 }
15694
15695 void UnmapGameButtonsOnTape()
15696 {
15697   UnmapGameButtonsExt(TRUE);
15698 }
15699
15700 void RedrawGameButtonsOnTape()
15701 {
15702   RedrawGameButtonsExt(TRUE);
15703 }
15704
15705 void GameUndoRedoExt()
15706 {
15707   ClearPlayerAction();
15708
15709   tape.pausing = TRUE;
15710
15711   RedrawPlayfield();
15712   UpdateAndDisplayGameControlValues();
15713
15714   DrawCompleteVideoDisplay();
15715   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15716   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15717   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15718
15719   BackToFront();
15720 }
15721
15722 void GameUndo(int steps)
15723 {
15724   if (!CheckEngineSnapshotList())
15725     return;
15726
15727   LoadEngineSnapshot_Undo(steps);
15728
15729   GameUndoRedoExt();
15730 }
15731
15732 void GameRedo(int steps)
15733 {
15734   if (!CheckEngineSnapshotList())
15735     return;
15736
15737   LoadEngineSnapshot_Redo(steps);
15738
15739   GameUndoRedoExt();
15740 }
15741
15742 static void HandleGameButtonsExt(int id, int button)
15743 {
15744   static boolean game_undo_executed = FALSE;
15745   int steps = BUTTON_STEPSIZE(button);
15746   boolean handle_game_buttons =
15747     (game_status == GAME_MODE_PLAYING ||
15748      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15749
15750   if (!handle_game_buttons)
15751     return;
15752
15753   switch (id)
15754   {
15755     case GAME_CTRL_ID_STOP:
15756     case GAME_CTRL_ID_PANEL_STOP:
15757       if (game_status == GAME_MODE_MAIN)
15758         break;
15759
15760       if (tape.playing)
15761         TapeStop();
15762       else
15763         RequestQuitGame(TRUE);
15764
15765       break;
15766
15767     case GAME_CTRL_ID_PAUSE:
15768     case GAME_CTRL_ID_PAUSE2:
15769     case GAME_CTRL_ID_PANEL_PAUSE:
15770       if (network.enabled && game_status == GAME_MODE_PLAYING)
15771       {
15772         if (tape.pausing)
15773           SendToServer_ContinuePlaying();
15774         else
15775           SendToServer_PausePlaying();
15776       }
15777       else
15778         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15779
15780       game_undo_executed = FALSE;
15781
15782       break;
15783
15784     case GAME_CTRL_ID_PLAY:
15785     case GAME_CTRL_ID_PANEL_PLAY:
15786       if (game_status == GAME_MODE_MAIN)
15787       {
15788         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15789       }
15790       else if (tape.pausing)
15791       {
15792         if (network.enabled)
15793           SendToServer_ContinuePlaying();
15794         else
15795           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15796       }
15797       break;
15798
15799     case GAME_CTRL_ID_UNDO:
15800       // Important: When using "save snapshot when collecting an item" mode,
15801       // load last (current) snapshot for first "undo" after pressing "pause"
15802       // (else the last-but-one snapshot would be loaded, because the snapshot
15803       // pointer already points to the last snapshot when pressing "pause",
15804       // which is fine for "every step/move" mode, but not for "every collect")
15805       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15806           !game_undo_executed)
15807         steps--;
15808
15809       game_undo_executed = TRUE;
15810
15811       GameUndo(steps);
15812       break;
15813
15814     case GAME_CTRL_ID_REDO:
15815       GameRedo(steps);
15816       break;
15817
15818     case GAME_CTRL_ID_SAVE:
15819       TapeQuickSave();
15820       break;
15821
15822     case GAME_CTRL_ID_LOAD:
15823       TapeQuickLoad();
15824       break;
15825
15826     case SOUND_CTRL_ID_MUSIC:
15827     case SOUND_CTRL_ID_PANEL_MUSIC:
15828       if (setup.sound_music)
15829       { 
15830         setup.sound_music = FALSE;
15831
15832         FadeMusic();
15833       }
15834       else if (audio.music_available)
15835       { 
15836         setup.sound = setup.sound_music = TRUE;
15837
15838         SetAudioMode(setup.sound);
15839
15840         if (game_status == GAME_MODE_PLAYING)
15841           PlayLevelMusic();
15842       }
15843
15844       RedrawSoundButtonGadget(id);
15845
15846       break;
15847
15848     case SOUND_CTRL_ID_LOOPS:
15849     case SOUND_CTRL_ID_PANEL_LOOPS:
15850       if (setup.sound_loops)
15851         setup.sound_loops = FALSE;
15852       else if (audio.loops_available)
15853       {
15854         setup.sound = setup.sound_loops = TRUE;
15855
15856         SetAudioMode(setup.sound);
15857       }
15858
15859       RedrawSoundButtonGadget(id);
15860
15861       break;
15862
15863     case SOUND_CTRL_ID_SIMPLE:
15864     case SOUND_CTRL_ID_PANEL_SIMPLE:
15865       if (setup.sound_simple)
15866         setup.sound_simple = FALSE;
15867       else if (audio.sound_available)
15868       {
15869         setup.sound = setup.sound_simple = TRUE;
15870
15871         SetAudioMode(setup.sound);
15872       }
15873
15874       RedrawSoundButtonGadget(id);
15875
15876       break;
15877
15878     default:
15879       break;
15880   }
15881 }
15882
15883 static void HandleGameButtons(struct GadgetInfo *gi)
15884 {
15885   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15886 }
15887
15888 void HandleSoundButtonKeys(Key key)
15889 {
15890   if (key == setup.shortcut.sound_simple)
15891     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15892   else if (key == setup.shortcut.sound_loops)
15893     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15894   else if (key == setup.shortcut.sound_music)
15895     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15896 }