added separate network info structure for network options and setup values
[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 #if defined(NETWORK_AVALIABLE)
3507   /* initial null action */
3508   if (network_playing)
3509     SendToServer_MovePlayer(MV_NONE);
3510 #endif
3511
3512   ZX = ZY = -1;
3513   ExitX = ExitY = -1;
3514
3515   FrameCounter = 0;
3516   TimeFrames = 0;
3517   TimePlayed = 0;
3518   TimeLeft = level.time;
3519   TapeTime = 0;
3520
3521   ScreenMovDir = MV_NONE;
3522   ScreenMovPos = 0;
3523   ScreenGfxPos = 0;
3524
3525   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3526
3527   AllPlayersGone = FALSE;
3528
3529   game.no_time_limit = (level.time == 0);
3530
3531   game.yamyam_content_nr = 0;
3532   game.robot_wheel_active = FALSE;
3533   game.magic_wall_active = FALSE;
3534   game.magic_wall_time_left = 0;
3535   game.light_time_left = 0;
3536   game.timegate_time_left = 0;
3537   game.switchgate_pos = 0;
3538   game.wind_direction = level.wind_direction_initial;
3539
3540   game.lenses_time_left = 0;
3541   game.magnify_time_left = 0;
3542
3543   game.ball_state = level.ball_state_initial;
3544   game.ball_content_nr = 0;
3545
3546   game.envelope_active = FALSE;
3547
3548   for (i = 0; i < NUM_BELTS; i++)
3549   {
3550     game.belt_dir[i] = MV_NONE;
3551     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3552   }
3553
3554   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3555     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3556
3557 #if DEBUG_INIT_PLAYER
3558   if (options.debug)
3559   {
3560     printf("Player status at level initialization:\n");
3561
3562     for (i = 0; i < MAX_PLAYERS; i++)
3563     {
3564       struct PlayerInfo *player = &stored_player[i];
3565
3566       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3567              i + 1,
3568              player->present,
3569              player->connected,
3570              player->connected_locally,
3571              player->connected_network,
3572              player->active);
3573
3574       if (local_player == player)
3575         printf(" (local player)");
3576
3577       printf("\n");
3578     }
3579   }
3580 #endif
3581
3582   SCAN_PLAYFIELD(x, y)
3583   {
3584     Feld[x][y] = level.field[x][y];
3585     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3586     ChangeDelay[x][y] = 0;
3587     ChangePage[x][y] = -1;
3588     CustomValue[x][y] = 0;              /* initialized in InitField() */
3589     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3590     AmoebaNr[x][y] = 0;
3591     WasJustMoving[x][y] = 0;
3592     WasJustFalling[x][y] = 0;
3593     CheckCollision[x][y] = 0;
3594     CheckImpact[x][y] = 0;
3595     Stop[x][y] = FALSE;
3596     Pushed[x][y] = FALSE;
3597
3598     ChangeCount[x][y] = 0;
3599     ChangeEvent[x][y] = -1;
3600
3601     ExplodePhase[x][y] = 0;
3602     ExplodeDelay[x][y] = 0;
3603     ExplodeField[x][y] = EX_TYPE_NONE;
3604
3605     RunnerVisit[x][y] = 0;
3606     PlayerVisit[x][y] = 0;
3607
3608     GfxFrame[x][y] = 0;
3609     GfxRandom[x][y] = INIT_GFX_RANDOM();
3610     GfxElement[x][y] = EL_UNDEFINED;
3611     GfxAction[x][y] = ACTION_DEFAULT;
3612     GfxDir[x][y] = MV_NONE;
3613     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3614   }
3615
3616   SCAN_PLAYFIELD(x, y)
3617   {
3618     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3619       emulate_bd = FALSE;
3620     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3621       emulate_sb = FALSE;
3622     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3623       emulate_sp = FALSE;
3624
3625     InitField(x, y, TRUE);
3626
3627     ResetGfxAnimation(x, y);
3628   }
3629
3630   InitBeltMovement();
3631
3632   for (i = 0; i < MAX_PLAYERS; i++)
3633   {
3634     struct PlayerInfo *player = &stored_player[i];
3635
3636     /* set number of special actions for bored and sleeping animation */
3637     player->num_special_action_bored =
3638       get_num_special_action(player->artwork_element,
3639                              ACTION_BORING_1, ACTION_BORING_LAST);
3640     player->num_special_action_sleeping =
3641       get_num_special_action(player->artwork_element,
3642                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3643   }
3644
3645   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3646                     emulate_sb ? EMU_SOKOBAN :
3647                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3648
3649   /* initialize type of slippery elements */
3650   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3651   {
3652     if (!IS_CUSTOM_ELEMENT(i))
3653     {
3654       /* default: elements slip down either to the left or right randomly */
3655       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3656
3657       /* SP style elements prefer to slip down on the left side */
3658       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3659         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3660
3661       /* BD style elements prefer to slip down on the left side */
3662       if (game.emulation == EMU_BOULDERDASH)
3663         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3664     }
3665   }
3666
3667   /* initialize explosion and ignition delay */
3668   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3669   {
3670     if (!IS_CUSTOM_ELEMENT(i))
3671     {
3672       int num_phase = 8;
3673       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3674                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3675                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3676       int last_phase = (num_phase + 1) * delay;
3677       int half_phase = (num_phase / 2) * delay;
3678
3679       element_info[i].explosion_delay = last_phase - 1;
3680       element_info[i].ignition_delay = half_phase;
3681
3682       if (i == EL_BLACK_ORB)
3683         element_info[i].ignition_delay = 1;
3684     }
3685   }
3686
3687   /* correct non-moving belts to start moving left */
3688   for (i = 0; i < NUM_BELTS; i++)
3689     if (game.belt_dir[i] == MV_NONE)
3690       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3691
3692 #if USE_NEW_PLAYER_ASSIGNMENTS
3693   for (i = 0; i < MAX_PLAYERS; i++)
3694   {
3695     stored_player[i].connected = FALSE;
3696
3697     /* in network game mode, the local player might not be the first player */
3698     if (stored_player[i].connected_locally)
3699       local_player = &stored_player[i];
3700   }
3701
3702   if (!network.enabled)
3703     local_player->connected = TRUE;
3704
3705   if (tape.playing)
3706   {
3707     for (i = 0; i < MAX_PLAYERS; i++)
3708       stored_player[i].connected = tape.player_participates[i];
3709   }
3710   else if (network.enabled)
3711   {
3712     /* add team mode players connected over the network (needed for correct
3713        assignment of player figures from level to locally playing players) */
3714
3715     for (i = 0; i < MAX_PLAYERS; i++)
3716       if (stored_player[i].connected_network)
3717         stored_player[i].connected = TRUE;
3718   }
3719   else if (game.team_mode)
3720   {
3721     /* try to guess locally connected team mode players (needed for correct
3722        assignment of player figures from level to locally playing players) */
3723
3724     for (i = 0; i < MAX_PLAYERS; i++)
3725       if (setup.input[i].use_joystick ||
3726           setup.input[i].key.left != KSYM_UNDEFINED)
3727         stored_player[i].connected = TRUE;
3728   }
3729
3730 #if DEBUG_INIT_PLAYER
3731   if (options.debug)
3732   {
3733     printf("Player status after level initialization:\n");
3734
3735     for (i = 0; i < MAX_PLAYERS; i++)
3736     {
3737       struct PlayerInfo *player = &stored_player[i];
3738
3739       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3740              i + 1,
3741              player->present,
3742              player->connected,
3743              player->connected_locally,
3744              player->connected_network,
3745              player->active);
3746
3747       if (local_player == player)
3748         printf(" (local player)");
3749
3750       printf("\n");
3751     }
3752   }
3753 #endif
3754
3755 #if DEBUG_INIT_PLAYER
3756   if (options.debug)
3757     printf("Reassigning players ...\n");
3758 #endif
3759
3760   /* check if any connected player was not found in playfield */
3761   for (i = 0; i < MAX_PLAYERS; i++)
3762   {
3763     struct PlayerInfo *player = &stored_player[i];
3764
3765     if (player->connected && !player->present)
3766     {
3767       struct PlayerInfo *field_player = NULL;
3768
3769 #if DEBUG_INIT_PLAYER
3770       if (options.debug)
3771         printf("- looking for field player for player %d ...\n", i + 1);
3772 #endif
3773
3774       /* assign first free player found that is present in the playfield */
3775
3776       /* first try: look for unmapped playfield player that is not connected */
3777       for (j = 0; j < MAX_PLAYERS; j++)
3778         if (field_player == NULL &&
3779             stored_player[j].present &&
3780             !stored_player[j].mapped &&
3781             !stored_player[j].connected)
3782           field_player = &stored_player[j];
3783
3784       /* second try: look for *any* unmapped playfield player */
3785       for (j = 0; j < MAX_PLAYERS; j++)
3786         if (field_player == NULL &&
3787             stored_player[j].present &&
3788             !stored_player[j].mapped)
3789           field_player = &stored_player[j];
3790
3791       if (field_player != NULL)
3792       {
3793         int jx = field_player->jx, jy = field_player->jy;
3794
3795 #if DEBUG_INIT_PLAYER
3796         if (options.debug)
3797           printf("- found player %d\n", field_player->index_nr + 1);
3798 #endif
3799
3800         player->present = FALSE;
3801         player->active = FALSE;
3802
3803         field_player->present = TRUE;
3804         field_player->active = TRUE;
3805
3806         /*
3807         player->initial_element = field_player->initial_element;
3808         player->artwork_element = field_player->artwork_element;
3809
3810         player->block_last_field       = field_player->block_last_field;
3811         player->block_delay_adjustment = field_player->block_delay_adjustment;
3812         */
3813
3814         StorePlayer[jx][jy] = field_player->element_nr;
3815
3816         field_player->jx = field_player->last_jx = jx;
3817         field_player->jy = field_player->last_jy = jy;
3818
3819         if (local_player == player)
3820           local_player = field_player;
3821
3822         map_player_action[field_player->index_nr] = i;
3823
3824         field_player->mapped = TRUE;
3825
3826 #if DEBUG_INIT_PLAYER
3827         if (options.debug)
3828           printf("- map_player_action[%d] == %d\n",
3829                  field_player->index_nr + 1, i + 1);
3830 #endif
3831       }
3832     }
3833
3834     if (player->connected && player->present)
3835       player->mapped = TRUE;
3836   }
3837
3838 #if DEBUG_INIT_PLAYER
3839   if (options.debug)
3840   {
3841     printf("Player status after player assignment (first stage):\n");
3842
3843     for (i = 0; i < MAX_PLAYERS; i++)
3844     {
3845       struct PlayerInfo *player = &stored_player[i];
3846
3847       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3848              i + 1,
3849              player->present,
3850              player->connected,
3851              player->connected_locally,
3852              player->connected_network,
3853              player->active);
3854
3855       if (local_player == player)
3856         printf(" (local player)");
3857
3858       printf("\n");
3859     }
3860   }
3861 #endif
3862
3863 #else
3864
3865   /* check if any connected player was not found in playfield */
3866   for (i = 0; i < MAX_PLAYERS; i++)
3867   {
3868     struct PlayerInfo *player = &stored_player[i];
3869
3870     if (player->connected && !player->present)
3871     {
3872       for (j = 0; j < MAX_PLAYERS; j++)
3873       {
3874         struct PlayerInfo *field_player = &stored_player[j];
3875         int jx = field_player->jx, jy = field_player->jy;
3876
3877         /* assign first free player found that is present in the playfield */
3878         if (field_player->present && !field_player->connected)
3879         {
3880           player->present = TRUE;
3881           player->active = TRUE;
3882
3883           field_player->present = FALSE;
3884           field_player->active = FALSE;
3885
3886           player->initial_element = field_player->initial_element;
3887           player->artwork_element = field_player->artwork_element;
3888
3889           player->block_last_field       = field_player->block_last_field;
3890           player->block_delay_adjustment = field_player->block_delay_adjustment;
3891
3892           StorePlayer[jx][jy] = player->element_nr;
3893
3894           player->jx = player->last_jx = jx;
3895           player->jy = player->last_jy = jy;
3896
3897           break;
3898         }
3899       }
3900     }
3901   }
3902 #endif
3903
3904 #if 0
3905   printf("::: local_player->present == %d\n", local_player->present);
3906 #endif
3907
3908   /* set focus to local player for network games, else to all players */
3909   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3910   game.centered_player_nr_next = game.centered_player_nr;
3911   game.set_centered_player = FALSE;
3912
3913   if (network_playing && tape.recording)
3914   {
3915     /* store client dependent player focus when recording network games */
3916     tape.centered_player_nr_next = game.centered_player_nr_next;
3917     tape.set_centered_player = TRUE;
3918   }
3919
3920   if (tape.playing)
3921   {
3922     /* when playing a tape, eliminate all players who do not participate */
3923
3924 #if USE_NEW_PLAYER_ASSIGNMENTS
3925
3926     if (!game.team_mode)
3927     {
3928       for (i = 0; i < MAX_PLAYERS; i++)
3929       {
3930         if (stored_player[i].active &&
3931             !tape.player_participates[map_player_action[i]])
3932         {
3933           struct PlayerInfo *player = &stored_player[i];
3934           int jx = player->jx, jy = player->jy;
3935
3936 #if DEBUG_INIT_PLAYER
3937           if (options.debug)
3938             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3939 #endif
3940
3941           player->active = FALSE;
3942           StorePlayer[jx][jy] = 0;
3943           Feld[jx][jy] = EL_EMPTY;
3944         }
3945       }
3946     }
3947
3948 #else
3949
3950     for (i = 0; i < MAX_PLAYERS; i++)
3951     {
3952       if (stored_player[i].active &&
3953           !tape.player_participates[i])
3954       {
3955         struct PlayerInfo *player = &stored_player[i];
3956         int jx = player->jx, jy = player->jy;
3957
3958         player->active = FALSE;
3959         StorePlayer[jx][jy] = 0;
3960         Feld[jx][jy] = EL_EMPTY;
3961       }
3962     }
3963 #endif
3964   }
3965   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3966   {
3967     /* when in single player mode, eliminate all but the first active player */
3968
3969     for (i = 0; i < MAX_PLAYERS; i++)
3970     {
3971       if (stored_player[i].active)
3972       {
3973         for (j = i + 1; j < MAX_PLAYERS; j++)
3974         {
3975           if (stored_player[j].active)
3976           {
3977             struct PlayerInfo *player = &stored_player[j];
3978             int jx = player->jx, jy = player->jy;
3979
3980             player->active = FALSE;
3981             player->present = FALSE;
3982
3983             StorePlayer[jx][jy] = 0;
3984             Feld[jx][jy] = EL_EMPTY;
3985           }
3986         }
3987       }
3988     }
3989   }
3990
3991   /* when recording the game, store which players take part in the game */
3992   if (tape.recording)
3993   {
3994 #if USE_NEW_PLAYER_ASSIGNMENTS
3995     for (i = 0; i < MAX_PLAYERS; i++)
3996       if (stored_player[i].connected)
3997         tape.player_participates[i] = TRUE;
3998 #else
3999     for (i = 0; i < MAX_PLAYERS; i++)
4000       if (stored_player[i].active)
4001         tape.player_participates[i] = TRUE;
4002 #endif
4003   }
4004
4005 #if DEBUG_INIT_PLAYER
4006   if (options.debug)
4007   {
4008     printf("Player status after player assignment (final stage):\n");
4009
4010     for (i = 0; i < MAX_PLAYERS; i++)
4011     {
4012       struct PlayerInfo *player = &stored_player[i];
4013
4014       printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4015              i + 1,
4016              player->present,
4017              player->connected,
4018              player->connected_locally,
4019              player->connected_network,
4020              player->active);
4021
4022       if (local_player == player)
4023         printf(" (local player)");
4024
4025       printf("\n");
4026     }
4027   }
4028 #endif
4029
4030   if (BorderElement == EL_EMPTY)
4031   {
4032     SBX_Left = 0;
4033     SBX_Right = lev_fieldx - SCR_FIELDX;
4034     SBY_Upper = 0;
4035     SBY_Lower = lev_fieldy - SCR_FIELDY;
4036   }
4037   else
4038   {
4039     SBX_Left = -1;
4040     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4041     SBY_Upper = -1;
4042     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4043   }
4044
4045   if (full_lev_fieldx <= SCR_FIELDX)
4046     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4047   if (full_lev_fieldy <= SCR_FIELDY)
4048     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4049
4050   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4051     SBX_Left--;
4052   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4053     SBY_Upper--;
4054
4055   /* if local player not found, look for custom element that might create
4056      the player (make some assumptions about the right custom element) */
4057   if (!local_player->present)
4058   {
4059     int start_x = 0, start_y = 0;
4060     int found_rating = 0;
4061     int found_element = EL_UNDEFINED;
4062     int player_nr = local_player->index_nr;
4063
4064     SCAN_PLAYFIELD(x, y)
4065     {
4066       int element = Feld[x][y];
4067       int content;
4068       int xx, yy;
4069       boolean is_player;
4070
4071       if (level.use_start_element[player_nr] &&
4072           level.start_element[player_nr] == element &&
4073           found_rating < 4)
4074       {
4075         start_x = x;
4076         start_y = y;
4077
4078         found_rating = 4;
4079         found_element = element;
4080       }
4081
4082       if (!IS_CUSTOM_ELEMENT(element))
4083         continue;
4084
4085       if (CAN_CHANGE(element))
4086       {
4087         for (i = 0; i < element_info[element].num_change_pages; i++)
4088         {
4089           /* check for player created from custom element as single target */
4090           content = element_info[element].change_page[i].target_element;
4091           is_player = ELEM_IS_PLAYER(content);
4092
4093           if (is_player && (found_rating < 3 ||
4094                             (found_rating == 3 && element < found_element)))
4095           {
4096             start_x = x;
4097             start_y = y;
4098
4099             found_rating = 3;
4100             found_element = element;
4101           }
4102         }
4103       }
4104
4105       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4106       {
4107         /* check for player created from custom element as explosion content */
4108         content = element_info[element].content.e[xx][yy];
4109         is_player = ELEM_IS_PLAYER(content);
4110
4111         if (is_player && (found_rating < 2 ||
4112                           (found_rating == 2 && element < found_element)))
4113         {
4114           start_x = x + xx - 1;
4115           start_y = y + yy - 1;
4116
4117           found_rating = 2;
4118           found_element = element;
4119         }
4120
4121         if (!CAN_CHANGE(element))
4122           continue;
4123
4124         for (i = 0; i < element_info[element].num_change_pages; i++)
4125         {
4126           /* check for player created from custom element as extended target */
4127           content =
4128             element_info[element].change_page[i].target_content.e[xx][yy];
4129
4130           is_player = ELEM_IS_PLAYER(content);
4131
4132           if (is_player && (found_rating < 1 ||
4133                             (found_rating == 1 && element < found_element)))
4134           {
4135             start_x = x + xx - 1;
4136             start_y = y + yy - 1;
4137
4138             found_rating = 1;
4139             found_element = element;
4140           }
4141         }
4142       }
4143     }
4144
4145     scroll_x = SCROLL_POSITION_X(start_x);
4146     scroll_y = SCROLL_POSITION_Y(start_y);
4147   }
4148   else
4149   {
4150     scroll_x = SCROLL_POSITION_X(local_player->jx);
4151     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4152   }
4153
4154   /* !!! FIX THIS (START) !!! */
4155   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4156   {
4157     InitGameEngine_EM();
4158   }
4159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4160   {
4161     InitGameEngine_SP();
4162   }
4163   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4164   {
4165     InitGameEngine_MM();
4166   }
4167   else
4168   {
4169     DrawLevel(REDRAW_FIELD);
4170     DrawAllPlayers();
4171
4172     /* after drawing the level, correct some elements */
4173     if (game.timegate_time_left == 0)
4174       CloseAllOpenTimegates();
4175   }
4176
4177   /* blit playfield from scroll buffer to normal back buffer for fading in */
4178   BlitScreenToBitmap(backbuffer);
4179   /* !!! FIX THIS (END) !!! */
4180
4181   DrawMaskedBorder(fade_mask);
4182
4183   FadeIn(fade_mask);
4184
4185 #if 1
4186   // full screen redraw is required at this point in the following cases:
4187   // - special editor door undrawn when game was started from level editor
4188   // - drawing area (playfield) was changed and has to be removed completely
4189   redraw_mask = REDRAW_ALL;
4190   BackToFront();
4191 #endif
4192
4193   if (!game.restart_level)
4194   {
4195     /* copy default game door content to main double buffer */
4196
4197     /* !!! CHECK AGAIN !!! */
4198     SetPanelBackground();
4199     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4200     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4201   }
4202
4203   SetPanelBackground();
4204   SetDrawBackgroundMask(REDRAW_DOOR_1);
4205
4206   UpdateAndDisplayGameControlValues();
4207
4208   if (!game.restart_level)
4209   {
4210     UnmapGameButtons();
4211     UnmapTapeButtons();
4212
4213     FreeGameButtons();
4214     CreateGameButtons();
4215
4216     MapGameButtons();
4217     MapTapeButtons();
4218
4219     /* copy actual game door content to door double buffer for OpenDoor() */
4220     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4221
4222     OpenDoor(DOOR_OPEN_ALL);
4223
4224     KeyboardAutoRepeatOffUnlessAutoplay();
4225
4226 #if DEBUG_INIT_PLAYER
4227     if (options.debug)
4228     {
4229       printf("Player status (final):\n");
4230
4231       for (i = 0; i < MAX_PLAYERS; i++)
4232       {
4233         struct PlayerInfo *player = &stored_player[i];
4234
4235         printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
4236                i + 1,
4237                player->present,
4238                player->connected,
4239                player->connected_locally,
4240                player->connected_network,
4241                player->active);
4242
4243         if (local_player == player)
4244           printf(" (local player)");
4245
4246         printf("\n");
4247       }
4248     }
4249 #endif
4250   }
4251
4252   UnmapAllGadgets();
4253
4254   MapGameButtons();
4255   MapTapeButtons();
4256
4257   if (!game.restart_level && !tape.playing)
4258   {
4259     LevelStats_incPlayed(level_nr);
4260
4261     SaveLevelSetup_SeriesInfo();
4262   }
4263
4264   game.restart_level = FALSE;
4265   game.restart_game_message = NULL;
4266
4267   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4268     InitGameActions_MM();
4269
4270   SaveEngineSnapshotToListInitial();
4271
4272   if (!game.restart_level)
4273   {
4274     PlaySound(SND_GAME_STARTING);
4275
4276     if (setup.sound_music)
4277       PlayLevelMusic();
4278   }
4279 }
4280
4281 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4282                         int actual_player_x, int actual_player_y)
4283 {
4284   /* this is used for non-R'n'D game engines to update certain engine values */
4285
4286   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4287   {
4288     actual_player_x = correctLevelPosX_EM(actual_player_x);
4289     actual_player_y = correctLevelPosY_EM(actual_player_y);
4290   }
4291
4292   /* needed to determine if sounds are played within the visible screen area */
4293   scroll_x = actual_scroll_x;
4294   scroll_y = actual_scroll_y;
4295
4296   /* needed to get player position for "follow finger" playing input method */
4297   local_player->jx = actual_player_x;
4298   local_player->jy = actual_player_y;
4299 }
4300
4301 void InitMovDir(int x, int y)
4302 {
4303   int i, element = Feld[x][y];
4304   static int xy[4][2] =
4305   {
4306     {  0, +1 },
4307     { +1,  0 },
4308     {  0, -1 },
4309     { -1,  0 }
4310   };
4311   static int direction[3][4] =
4312   {
4313     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4314     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4315     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4316   };
4317
4318   switch (element)
4319   {
4320     case EL_BUG_RIGHT:
4321     case EL_BUG_UP:
4322     case EL_BUG_LEFT:
4323     case EL_BUG_DOWN:
4324       Feld[x][y] = EL_BUG;
4325       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4326       break;
4327
4328     case EL_SPACESHIP_RIGHT:
4329     case EL_SPACESHIP_UP:
4330     case EL_SPACESHIP_LEFT:
4331     case EL_SPACESHIP_DOWN:
4332       Feld[x][y] = EL_SPACESHIP;
4333       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4334       break;
4335
4336     case EL_BD_BUTTERFLY_RIGHT:
4337     case EL_BD_BUTTERFLY_UP:
4338     case EL_BD_BUTTERFLY_LEFT:
4339     case EL_BD_BUTTERFLY_DOWN:
4340       Feld[x][y] = EL_BD_BUTTERFLY;
4341       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4342       break;
4343
4344     case EL_BD_FIREFLY_RIGHT:
4345     case EL_BD_FIREFLY_UP:
4346     case EL_BD_FIREFLY_LEFT:
4347     case EL_BD_FIREFLY_DOWN:
4348       Feld[x][y] = EL_BD_FIREFLY;
4349       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4350       break;
4351
4352     case EL_PACMAN_RIGHT:
4353     case EL_PACMAN_UP:
4354     case EL_PACMAN_LEFT:
4355     case EL_PACMAN_DOWN:
4356       Feld[x][y] = EL_PACMAN;
4357       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4358       break;
4359
4360     case EL_YAMYAM_LEFT:
4361     case EL_YAMYAM_RIGHT:
4362     case EL_YAMYAM_UP:
4363     case EL_YAMYAM_DOWN:
4364       Feld[x][y] = EL_YAMYAM;
4365       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4366       break;
4367
4368     case EL_SP_SNIKSNAK:
4369       MovDir[x][y] = MV_UP;
4370       break;
4371
4372     case EL_SP_ELECTRON:
4373       MovDir[x][y] = MV_LEFT;
4374       break;
4375
4376     case EL_MOLE_LEFT:
4377     case EL_MOLE_RIGHT:
4378     case EL_MOLE_UP:
4379     case EL_MOLE_DOWN:
4380       Feld[x][y] = EL_MOLE;
4381       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4382       break;
4383
4384     default:
4385       if (IS_CUSTOM_ELEMENT(element))
4386       {
4387         struct ElementInfo *ei = &element_info[element];
4388         int move_direction_initial = ei->move_direction_initial;
4389         int move_pattern = ei->move_pattern;
4390
4391         if (move_direction_initial == MV_START_PREVIOUS)
4392         {
4393           if (MovDir[x][y] != MV_NONE)
4394             return;
4395
4396           move_direction_initial = MV_START_AUTOMATIC;
4397         }
4398
4399         if (move_direction_initial == MV_START_RANDOM)
4400           MovDir[x][y] = 1 << RND(4);
4401         else if (move_direction_initial & MV_ANY_DIRECTION)
4402           MovDir[x][y] = move_direction_initial;
4403         else if (move_pattern == MV_ALL_DIRECTIONS ||
4404                  move_pattern == MV_TURNING_LEFT ||
4405                  move_pattern == MV_TURNING_RIGHT ||
4406                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4407                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4408                  move_pattern == MV_TURNING_RANDOM)
4409           MovDir[x][y] = 1 << RND(4);
4410         else if (move_pattern == MV_HORIZONTAL)
4411           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4412         else if (move_pattern == MV_VERTICAL)
4413           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4414         else if (move_pattern & MV_ANY_DIRECTION)
4415           MovDir[x][y] = element_info[element].move_pattern;
4416         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4417                  move_pattern == MV_ALONG_RIGHT_SIDE)
4418         {
4419           /* use random direction as default start direction */
4420           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4421             MovDir[x][y] = 1 << RND(4);
4422
4423           for (i = 0; i < NUM_DIRECTIONS; i++)
4424           {
4425             int x1 = x + xy[i][0];
4426             int y1 = y + xy[i][1];
4427
4428             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4429             {
4430               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4431                 MovDir[x][y] = direction[0][i];
4432               else
4433                 MovDir[x][y] = direction[1][i];
4434
4435               break;
4436             }
4437           }
4438         }                
4439       }
4440       else
4441       {
4442         MovDir[x][y] = 1 << RND(4);
4443
4444         if (element != EL_BUG &&
4445             element != EL_SPACESHIP &&
4446             element != EL_BD_BUTTERFLY &&
4447             element != EL_BD_FIREFLY)
4448           break;
4449
4450         for (i = 0; i < NUM_DIRECTIONS; i++)
4451         {
4452           int x1 = x + xy[i][0];
4453           int y1 = y + xy[i][1];
4454
4455           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4456           {
4457             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4458             {
4459               MovDir[x][y] = direction[0][i];
4460               break;
4461             }
4462             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4463                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4464             {
4465               MovDir[x][y] = direction[1][i];
4466               break;
4467             }
4468           }
4469         }
4470       }
4471       break;
4472   }
4473
4474   GfxDir[x][y] = MovDir[x][y];
4475 }
4476
4477 void InitAmoebaNr(int x, int y)
4478 {
4479   int i;
4480   int group_nr = AmoebeNachbarNr(x, y);
4481
4482   if (group_nr == 0)
4483   {
4484     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4485     {
4486       if (AmoebaCnt[i] == 0)
4487       {
4488         group_nr = i;
4489         break;
4490       }
4491     }
4492   }
4493
4494   AmoebaNr[x][y] = group_nr;
4495   AmoebaCnt[group_nr]++;
4496   AmoebaCnt2[group_nr]++;
4497 }
4498
4499 static void PlayerWins(struct PlayerInfo *player)
4500 {
4501   player->LevelSolved = TRUE;
4502   player->GameOver = TRUE;
4503
4504   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4505                          level.native_em_level->lev->score :
4506                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4507                          game_mm.score :
4508                          player->score);
4509   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4510                           MM_HEALTH(game_mm.laser_overload_value) :
4511                           player->health);
4512
4513   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4514                                       TimeLeft);
4515   player->LevelSolved_CountingScore = player->score_final;
4516   player->LevelSolved_CountingHealth = player->health_final;
4517 }
4518
4519 void GameWon()
4520 {
4521   static int time_count_steps;
4522   static int time, time_final;
4523   static int score, score_final;
4524   static int health, health_final;
4525   static int game_over_delay_1 = 0;
4526   static int game_over_delay_2 = 0;
4527   static int game_over_delay_3 = 0;
4528   int game_over_delay_value_1 = 50;
4529   int game_over_delay_value_2 = 25;
4530   int game_over_delay_value_3 = 50;
4531
4532   if (!local_player->LevelSolved_GameWon)
4533   {
4534     int i;
4535
4536     /* do not start end game actions before the player stops moving (to exit) */
4537     if (local_player->MovPos)
4538       return;
4539
4540     local_player->LevelSolved_GameWon = TRUE;
4541     local_player->LevelSolved_SaveTape = tape.recording;
4542     local_player->LevelSolved_SaveScore = !tape.playing;
4543
4544     if (!tape.playing)
4545     {
4546       LevelStats_incSolved(level_nr);
4547
4548       SaveLevelSetup_SeriesInfo();
4549     }
4550
4551     if (tape.auto_play)         /* tape might already be stopped here */
4552       tape.auto_play_level_solved = TRUE;
4553
4554     TapeStop();
4555
4556     game_over_delay_1 = 0;
4557     game_over_delay_2 = 0;
4558     game_over_delay_3 = game_over_delay_value_3;
4559
4560     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4561     score = score_final = local_player->score_final;
4562     health = health_final = local_player->health_final;
4563
4564     if (level.score[SC_TIME_BONUS] > 0)
4565     {
4566       if (TimeLeft > 0)
4567       {
4568         time_final = 0;
4569         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4570       }
4571       else if (game.no_time_limit && TimePlayed < 999)
4572       {
4573         time_final = 999;
4574         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4575       }
4576
4577       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4578
4579       game_over_delay_1 = game_over_delay_value_1;
4580
4581       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4582       {
4583         health_final = 0;
4584         score_final += health * level.score[SC_TIME_BONUS];
4585
4586         game_over_delay_2 = game_over_delay_value_2;
4587       }
4588
4589       local_player->score_final = score_final;
4590       local_player->health_final = health_final;
4591     }
4592
4593     if (level_editor_test_game)
4594     {
4595       time = time_final;
4596       score = score_final;
4597
4598       local_player->LevelSolved_CountingTime = time;
4599       local_player->LevelSolved_CountingScore = score;
4600
4601       game_panel_controls[GAME_PANEL_TIME].value = time;
4602       game_panel_controls[GAME_PANEL_SCORE].value = score;
4603
4604       DisplayGameControlValues();
4605     }
4606
4607     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4608     {
4609       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4610       {
4611         /* close exit door after last player */
4612         if ((AllPlayersGone &&
4613              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4614               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4615               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4616             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4617             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4618         {
4619           int element = Feld[ExitX][ExitY];
4620
4621           Feld[ExitX][ExitY] =
4622             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4623              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4624              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4625              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4626              EL_EM_STEEL_EXIT_CLOSING);
4627
4628           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4629         }
4630
4631         /* player disappears */
4632         DrawLevelField(ExitX, ExitY);
4633       }
4634
4635       for (i = 0; i < MAX_PLAYERS; i++)
4636       {
4637         struct PlayerInfo *player = &stored_player[i];
4638
4639         if (player->present)
4640         {
4641           RemovePlayer(player);
4642
4643           /* player disappears */
4644           DrawLevelField(player->jx, player->jy);
4645         }
4646       }
4647     }
4648
4649     PlaySound(SND_GAME_WINNING);
4650   }
4651
4652   if (game_over_delay_1 > 0)
4653   {
4654     game_over_delay_1--;
4655
4656     return;
4657   }
4658
4659   if (time != time_final)
4660   {
4661     int time_to_go = ABS(time_final - time);
4662     int time_count_dir = (time < time_final ? +1 : -1);
4663
4664     if (time_to_go < time_count_steps)
4665       time_count_steps = 1;
4666
4667     time  += time_count_steps * time_count_dir;
4668     score += time_count_steps * level.score[SC_TIME_BONUS];
4669
4670     local_player->LevelSolved_CountingTime = time;
4671     local_player->LevelSolved_CountingScore = score;
4672
4673     game_panel_controls[GAME_PANEL_TIME].value = time;
4674     game_panel_controls[GAME_PANEL_SCORE].value = score;
4675
4676     DisplayGameControlValues();
4677
4678     if (time == time_final)
4679       StopSound(SND_GAME_LEVELTIME_BONUS);
4680     else if (setup.sound_loops)
4681       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4682     else
4683       PlaySound(SND_GAME_LEVELTIME_BONUS);
4684
4685     return;
4686   }
4687
4688   if (game_over_delay_2 > 0)
4689   {
4690     game_over_delay_2--;
4691
4692     return;
4693   }
4694
4695   if (health != health_final)
4696   {
4697     int health_count_dir = (health < health_final ? +1 : -1);
4698
4699     health += health_count_dir;
4700     score  += level.score[SC_TIME_BONUS];
4701
4702     local_player->LevelSolved_CountingHealth = health;
4703     local_player->LevelSolved_CountingScore = score;
4704
4705     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4706     game_panel_controls[GAME_PANEL_SCORE].value = score;
4707
4708     DisplayGameControlValues();
4709
4710     if (health == health_final)
4711       StopSound(SND_GAME_LEVELTIME_BONUS);
4712     else if (setup.sound_loops)
4713       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4714     else
4715       PlaySound(SND_GAME_LEVELTIME_BONUS);
4716
4717     return;
4718   }
4719
4720   local_player->LevelSolved_PanelOff = TRUE;
4721
4722   if (game_over_delay_3 > 0)
4723   {
4724     game_over_delay_3--;
4725
4726     return;
4727   }
4728
4729   GameEnd();
4730 }
4731
4732 void GameEnd()
4733 {
4734   int hi_pos;
4735   boolean raise_level = FALSE;
4736
4737   local_player->LevelSolved_GameEnd = TRUE;
4738
4739   if (local_player->LevelSolved_SaveTape)
4740   {
4741     /* make sure that request dialog to save tape does not open door again */
4742     if (!global.use_envelope_request)
4743       CloseDoor(DOOR_CLOSE_1);
4744
4745     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4746   }
4747
4748   /* if no tape is to be saved, close both doors simultaneously */
4749   CloseDoor(DOOR_CLOSE_ALL);
4750
4751   if (level_editor_test_game)
4752   {
4753     SetGameStatus(GAME_MODE_MAIN);
4754
4755     DrawMainMenu();
4756
4757     return;
4758   }
4759
4760   if (!local_player->LevelSolved_SaveScore)
4761   {
4762     SetGameStatus(GAME_MODE_MAIN);
4763
4764     DrawMainMenu();
4765
4766     return;
4767   }
4768
4769   if (level_nr == leveldir_current->handicap_level)
4770   {
4771     leveldir_current->handicap_level++;
4772
4773     SaveLevelSetup_SeriesInfo();
4774   }
4775
4776   if (setup.increment_levels &&
4777       level_nr < leveldir_current->last_level)
4778     raise_level = TRUE;                 /* advance to next level */
4779
4780   if ((hi_pos = NewHiScore()) >= 0) 
4781   {
4782     SetGameStatus(GAME_MODE_SCORES);
4783
4784     DrawHallOfFame(hi_pos);
4785
4786     if (raise_level)
4787     {
4788       level_nr++;
4789       TapeErase();
4790     }
4791   }
4792   else
4793   {
4794     SetGameStatus(GAME_MODE_MAIN);
4795
4796     if (raise_level)
4797     {
4798       level_nr++;
4799       TapeErase();
4800     }
4801
4802     DrawMainMenu();
4803   }
4804 }
4805
4806 int NewHiScore()
4807 {
4808   int k, l;
4809   int position = -1;
4810   boolean one_score_entry_per_name = !program.many_scores_per_name;
4811
4812   LoadScore(level_nr);
4813
4814   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4815       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4816     return -1;
4817
4818   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4819   {
4820     if (local_player->score_final > highscore[k].Score)
4821     {
4822       /* player has made it to the hall of fame */
4823
4824       if (k < MAX_SCORE_ENTRIES - 1)
4825       {
4826         int m = MAX_SCORE_ENTRIES - 1;
4827
4828         if (one_score_entry_per_name)
4829         {
4830           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4831             if (strEqual(setup.player_name, highscore[l].Name))
4832               m = l;
4833
4834           if (m == k)   /* player's new highscore overwrites his old one */
4835             goto put_into_list;
4836         }
4837
4838         for (l = m; l > k; l--)
4839         {
4840           strcpy(highscore[l].Name, highscore[l - 1].Name);
4841           highscore[l].Score = highscore[l - 1].Score;
4842         }
4843       }
4844
4845       put_into_list:
4846
4847       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4848       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4849       highscore[k].Score = local_player->score_final; 
4850       position = k;
4851
4852       break;
4853     }
4854     else if (one_score_entry_per_name &&
4855              !strncmp(setup.player_name, highscore[k].Name,
4856                       MAX_PLAYER_NAME_LEN))
4857       break;    /* player already there with a higher score */
4858   }
4859
4860   if (position >= 0) 
4861     SaveScore(level_nr);
4862
4863   return position;
4864 }
4865
4866 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4867 {
4868   int element = Feld[x][y];
4869   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4870   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4871   int horiz_move = (dx != 0);
4872   int sign = (horiz_move ? dx : dy);
4873   int step = sign * element_info[element].move_stepsize;
4874
4875   /* special values for move stepsize for spring and things on conveyor belt */
4876   if (horiz_move)
4877   {
4878     if (CAN_FALL(element) &&
4879         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4880       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4881     else if (element == EL_SPRING)
4882       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4883   }
4884
4885   return step;
4886 }
4887
4888 inline static int getElementMoveStepsize(int x, int y)
4889 {
4890   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4891 }
4892
4893 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4894 {
4895   if (player->GfxAction != action || player->GfxDir != dir)
4896   {
4897     player->GfxAction = action;
4898     player->GfxDir = dir;
4899     player->Frame = 0;
4900     player->StepFrame = 0;
4901   }
4902 }
4903
4904 static void ResetGfxFrame(int x, int y)
4905 {
4906   // profiling showed that "autotest" spends 10~20% of its time in this function
4907   if (DrawingDeactivatedField())
4908     return;
4909
4910   int element = Feld[x][y];
4911   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4912
4913   if (graphic_info[graphic].anim_global_sync)
4914     GfxFrame[x][y] = FrameCounter;
4915   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4916     GfxFrame[x][y] = CustomValue[x][y];
4917   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4918     GfxFrame[x][y] = element_info[element].collect_score;
4919   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4920     GfxFrame[x][y] = ChangeDelay[x][y];
4921 }
4922
4923 static void ResetGfxAnimation(int x, int y)
4924 {
4925   GfxAction[x][y] = ACTION_DEFAULT;
4926   GfxDir[x][y] = MovDir[x][y];
4927   GfxFrame[x][y] = 0;
4928
4929   ResetGfxFrame(x, y);
4930 }
4931
4932 static void ResetRandomAnimationValue(int x, int y)
4933 {
4934   GfxRandom[x][y] = INIT_GFX_RANDOM();
4935 }
4936
4937 void InitMovingField(int x, int y, int direction)
4938 {
4939   int element = Feld[x][y];
4940   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4941   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4942   int newx = x + dx;
4943   int newy = y + dy;
4944   boolean is_moving_before, is_moving_after;
4945
4946   /* check if element was/is moving or being moved before/after mode change */
4947   is_moving_before = (WasJustMoving[x][y] != 0);
4948   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4949
4950   /* reset animation only for moving elements which change direction of moving
4951      or which just started or stopped moving
4952      (else CEs with property "can move" / "not moving" are reset each frame) */
4953   if (is_moving_before != is_moving_after ||
4954       direction != MovDir[x][y])
4955     ResetGfxAnimation(x, y);
4956
4957   MovDir[x][y] = direction;
4958   GfxDir[x][y] = direction;
4959
4960   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4961                      direction == MV_DOWN && CAN_FALL(element) ?
4962                      ACTION_FALLING : ACTION_MOVING);
4963
4964   /* this is needed for CEs with property "can move" / "not moving" */
4965
4966   if (is_moving_after)
4967   {
4968     if (Feld[newx][newy] == EL_EMPTY)
4969       Feld[newx][newy] = EL_BLOCKED;
4970
4971     MovDir[newx][newy] = MovDir[x][y];
4972
4973     CustomValue[newx][newy] = CustomValue[x][y];
4974
4975     GfxFrame[newx][newy] = GfxFrame[x][y];
4976     GfxRandom[newx][newy] = GfxRandom[x][y];
4977     GfxAction[newx][newy] = GfxAction[x][y];
4978     GfxDir[newx][newy] = GfxDir[x][y];
4979   }
4980 }
4981
4982 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4983 {
4984   int direction = MovDir[x][y];
4985   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4986   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4987
4988   *goes_to_x = newx;
4989   *goes_to_y = newy;
4990 }
4991
4992 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4993 {
4994   int oldx = x, oldy = y;
4995   int direction = MovDir[x][y];
4996
4997   if (direction == MV_LEFT)
4998     oldx++;
4999   else if (direction == MV_RIGHT)
5000     oldx--;
5001   else if (direction == MV_UP)
5002     oldy++;
5003   else if (direction == MV_DOWN)
5004     oldy--;
5005
5006   *comes_from_x = oldx;
5007   *comes_from_y = oldy;
5008 }
5009
5010 int MovingOrBlocked2Element(int x, int y)
5011 {
5012   int element = Feld[x][y];
5013
5014   if (element == EL_BLOCKED)
5015   {
5016     int oldx, oldy;
5017
5018     Blocked2Moving(x, y, &oldx, &oldy);
5019     return Feld[oldx][oldy];
5020   }
5021   else
5022     return element;
5023 }
5024
5025 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5026 {
5027   /* like MovingOrBlocked2Element(), but if element is moving
5028      and (x,y) is the field the moving element is just leaving,
5029      return EL_BLOCKED instead of the element value */
5030   int element = Feld[x][y];
5031
5032   if (IS_MOVING(x, y))
5033   {
5034     if (element == EL_BLOCKED)
5035     {
5036       int oldx, oldy;
5037
5038       Blocked2Moving(x, y, &oldx, &oldy);
5039       return Feld[oldx][oldy];
5040     }
5041     else
5042       return EL_BLOCKED;
5043   }
5044   else
5045     return element;
5046 }
5047
5048 static void RemoveField(int x, int y)
5049 {
5050   Feld[x][y] = EL_EMPTY;
5051
5052   MovPos[x][y] = 0;
5053   MovDir[x][y] = 0;
5054   MovDelay[x][y] = 0;
5055
5056   CustomValue[x][y] = 0;
5057
5058   AmoebaNr[x][y] = 0;
5059   ChangeDelay[x][y] = 0;
5060   ChangePage[x][y] = -1;
5061   Pushed[x][y] = FALSE;
5062
5063   GfxElement[x][y] = EL_UNDEFINED;
5064   GfxAction[x][y] = ACTION_DEFAULT;
5065   GfxDir[x][y] = MV_NONE;
5066 }
5067
5068 void RemoveMovingField(int x, int y)
5069 {
5070   int oldx = x, oldy = y, newx = x, newy = y;
5071   int element = Feld[x][y];
5072   int next_element = EL_UNDEFINED;
5073
5074   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5075     return;
5076
5077   if (IS_MOVING(x, y))
5078   {
5079     Moving2Blocked(x, y, &newx, &newy);
5080
5081     if (Feld[newx][newy] != EL_BLOCKED)
5082     {
5083       /* element is moving, but target field is not free (blocked), but
5084          already occupied by something different (example: acid pool);
5085          in this case, only remove the moving field, but not the target */
5086
5087       RemoveField(oldx, oldy);
5088
5089       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5090
5091       TEST_DrawLevelField(oldx, oldy);
5092
5093       return;
5094     }
5095   }
5096   else if (element == EL_BLOCKED)
5097   {
5098     Blocked2Moving(x, y, &oldx, &oldy);
5099     if (!IS_MOVING(oldx, oldy))
5100       return;
5101   }
5102
5103   if (element == EL_BLOCKED &&
5104       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5105        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5106        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5107        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5108        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5109        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5110     next_element = get_next_element(Feld[oldx][oldy]);
5111
5112   RemoveField(oldx, oldy);
5113   RemoveField(newx, newy);
5114
5115   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5116
5117   if (next_element != EL_UNDEFINED)
5118     Feld[oldx][oldy] = next_element;
5119
5120   TEST_DrawLevelField(oldx, oldy);
5121   TEST_DrawLevelField(newx, newy);
5122 }
5123
5124 void DrawDynamite(int x, int y)
5125 {
5126   int sx = SCREENX(x), sy = SCREENY(y);
5127   int graphic = el2img(Feld[x][y]);
5128   int frame;
5129
5130   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5131     return;
5132
5133   if (IS_WALKABLE_INSIDE(Back[x][y]))
5134     return;
5135
5136   if (Back[x][y])
5137     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5138   else if (Store[x][y])
5139     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5140
5141   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5142
5143   if (Back[x][y] || Store[x][y])
5144     DrawGraphicThruMask(sx, sy, graphic, frame);
5145   else
5146     DrawGraphic(sx, sy, graphic, frame);
5147 }
5148
5149 void CheckDynamite(int x, int y)
5150 {
5151   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5152   {
5153     MovDelay[x][y]--;
5154
5155     if (MovDelay[x][y] != 0)
5156     {
5157       DrawDynamite(x, y);
5158       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5159
5160       return;
5161     }
5162   }
5163
5164   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5165
5166   Bang(x, y);
5167 }
5168
5169 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5170 {
5171   boolean num_checked_players = 0;
5172   int i;
5173
5174   for (i = 0; i < MAX_PLAYERS; i++)
5175   {
5176     if (stored_player[i].active)
5177     {
5178       int sx = stored_player[i].jx;
5179       int sy = stored_player[i].jy;
5180
5181       if (num_checked_players == 0)
5182       {
5183         *sx1 = *sx2 = sx;
5184         *sy1 = *sy2 = sy;
5185       }
5186       else
5187       {
5188         *sx1 = MIN(*sx1, sx);
5189         *sy1 = MIN(*sy1, sy);
5190         *sx2 = MAX(*sx2, sx);
5191         *sy2 = MAX(*sy2, sy);
5192       }
5193
5194       num_checked_players++;
5195     }
5196   }
5197 }
5198
5199 static boolean checkIfAllPlayersFitToScreen_RND()
5200 {
5201   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5202
5203   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5204
5205   return (sx2 - sx1 < SCR_FIELDX &&
5206           sy2 - sy1 < SCR_FIELDY);
5207 }
5208
5209 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5210 {
5211   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5212
5213   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5214
5215   *sx = (sx1 + sx2) / 2;
5216   *sy = (sy1 + sy2) / 2;
5217 }
5218
5219 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5220                         boolean center_screen, boolean quick_relocation)
5221 {
5222   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5223   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5224   boolean no_delay = (tape.warp_forward);
5225   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5226   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5227   int new_scroll_x, new_scroll_y;
5228
5229   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5230   {
5231     /* case 1: quick relocation inside visible screen (without scrolling) */
5232
5233     RedrawPlayfield();
5234
5235     return;
5236   }
5237
5238   if (!level.shifted_relocation || center_screen)
5239   {
5240     /* relocation _with_ centering of screen */
5241
5242     new_scroll_x = SCROLL_POSITION_X(x);
5243     new_scroll_y = SCROLL_POSITION_Y(y);
5244   }
5245   else
5246   {
5247     /* relocation _without_ centering of screen */
5248
5249     int center_scroll_x = SCROLL_POSITION_X(old_x);
5250     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5251     int offset_x = x + (scroll_x - center_scroll_x);
5252     int offset_y = y + (scroll_y - center_scroll_y);
5253
5254     /* for new screen position, apply previous offset to center position */
5255     new_scroll_x = SCROLL_POSITION_X(offset_x);
5256     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5257   }
5258
5259   if (quick_relocation)
5260   {
5261     /* case 2: quick relocation (redraw without visible scrolling) */
5262
5263     scroll_x = new_scroll_x;
5264     scroll_y = new_scroll_y;
5265
5266     RedrawPlayfield();
5267
5268     return;
5269   }
5270
5271   /* case 3: visible relocation (with scrolling to new position) */
5272
5273   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5274
5275   SetVideoFrameDelay(wait_delay_value);
5276
5277   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5278   {
5279     int dx = 0, dy = 0;
5280     int fx = FX, fy = FY;
5281
5282     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5283     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5284
5285     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5286       break;
5287
5288     scroll_x -= dx;
5289     scroll_y -= dy;
5290
5291     fx += dx * TILEX / 2;
5292     fy += dy * TILEY / 2;
5293
5294     ScrollLevel(dx, dy);
5295     DrawAllPlayers();
5296
5297     /* scroll in two steps of half tile size to make things smoother */
5298     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5299
5300     /* scroll second step to align at full tile size */
5301     BlitScreenToBitmap(window);
5302   }
5303
5304   DrawAllPlayers();
5305   BackToFront();
5306
5307   SetVideoFrameDelay(frame_delay_value_old);
5308 }
5309
5310 void RelocatePlayer(int jx, int jy, int el_player_raw)
5311 {
5312   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5313   int player_nr = GET_PLAYER_NR(el_player);
5314   struct PlayerInfo *player = &stored_player[player_nr];
5315   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5316   boolean no_delay = (tape.warp_forward);
5317   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5318   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5319   int old_jx = player->jx;
5320   int old_jy = player->jy;
5321   int old_element = Feld[old_jx][old_jy];
5322   int element = Feld[jx][jy];
5323   boolean player_relocated = (old_jx != jx || old_jy != jy);
5324
5325   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5326   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5327   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5328   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5329   int leave_side_horiz = move_dir_horiz;
5330   int leave_side_vert  = move_dir_vert;
5331   int enter_side = enter_side_horiz | enter_side_vert;
5332   int leave_side = leave_side_horiz | leave_side_vert;
5333
5334   if (player->GameOver)         /* do not reanimate dead player */
5335     return;
5336
5337   if (!player_relocated)        /* no need to relocate the player */
5338     return;
5339
5340   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5341   {
5342     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5343     DrawLevelField(jx, jy);
5344   }
5345
5346   if (player->present)
5347   {
5348     while (player->MovPos)
5349     {
5350       ScrollPlayer(player, SCROLL_GO_ON);
5351       ScrollScreen(NULL, SCROLL_GO_ON);
5352
5353       AdvanceFrameAndPlayerCounters(player->index_nr);
5354
5355       DrawPlayer(player);
5356
5357       BackToFront_WithFrameDelay(wait_delay_value);
5358     }
5359
5360     DrawPlayer(player);         /* needed here only to cleanup last field */
5361     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5362
5363     player->is_moving = FALSE;
5364   }
5365
5366   if (IS_CUSTOM_ELEMENT(old_element))
5367     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5368                                CE_LEFT_BY_PLAYER,
5369                                player->index_bit, leave_side);
5370
5371   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5372                                       CE_PLAYER_LEAVES_X,
5373                                       player->index_bit, leave_side);
5374
5375   Feld[jx][jy] = el_player;
5376   InitPlayerField(jx, jy, el_player, TRUE);
5377
5378   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5379      possible that the relocation target field did not contain a player element,
5380      but a walkable element, to which the new player was relocated -- in this
5381      case, restore that (already initialized!) element on the player field */
5382   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5383   {
5384     Feld[jx][jy] = element;     /* restore previously existing element */
5385   }
5386
5387   /* only visually relocate centered player */
5388   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5389                      FALSE, level.instant_relocation);
5390
5391   TestIfPlayerTouchesBadThing(jx, jy);
5392   TestIfPlayerTouchesCustomElement(jx, jy);
5393
5394   if (IS_CUSTOM_ELEMENT(element))
5395     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5396                                player->index_bit, enter_side);
5397
5398   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5399                                       player->index_bit, enter_side);
5400
5401   if (player->is_switching)
5402   {
5403     /* ensure that relocation while still switching an element does not cause
5404        a new element to be treated as also switched directly after relocation
5405        (this is important for teleporter switches that teleport the player to
5406        a place where another teleporter switch is in the same direction, which
5407        would then incorrectly be treated as immediately switched before the
5408        direction key that caused the switch was released) */
5409
5410     player->switch_x += jx - old_jx;
5411     player->switch_y += jy - old_jy;
5412   }
5413 }
5414
5415 void Explode(int ex, int ey, int phase, int mode)
5416 {
5417   int x, y;
5418   int last_phase;
5419   int border_element;
5420
5421   /* !!! eliminate this variable !!! */
5422   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5423
5424   if (game.explosions_delayed)
5425   {
5426     ExplodeField[ex][ey] = mode;
5427     return;
5428   }
5429
5430   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5431   {
5432     int center_element = Feld[ex][ey];
5433     int artwork_element, explosion_element;     /* set these values later */
5434
5435     /* remove things displayed in background while burning dynamite */
5436     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5437       Back[ex][ey] = 0;
5438
5439     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5440     {
5441       /* put moving element to center field (and let it explode there) */
5442       center_element = MovingOrBlocked2Element(ex, ey);
5443       RemoveMovingField(ex, ey);
5444       Feld[ex][ey] = center_element;
5445     }
5446
5447     /* now "center_element" is finally determined -- set related values now */
5448     artwork_element = center_element;           /* for custom player artwork */
5449     explosion_element = center_element;         /* for custom player artwork */
5450
5451     if (IS_PLAYER(ex, ey))
5452     {
5453       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5454
5455       artwork_element = stored_player[player_nr].artwork_element;
5456
5457       if (level.use_explosion_element[player_nr])
5458       {
5459         explosion_element = level.explosion_element[player_nr];
5460         artwork_element = explosion_element;
5461       }
5462     }
5463
5464     if (mode == EX_TYPE_NORMAL ||
5465         mode == EX_TYPE_CENTER ||
5466         mode == EX_TYPE_CROSS)
5467       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5468
5469     last_phase = element_info[explosion_element].explosion_delay + 1;
5470
5471     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5472     {
5473       int xx = x - ex + 1;
5474       int yy = y - ey + 1;
5475       int element;
5476
5477       if (!IN_LEV_FIELD(x, y) ||
5478           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5479           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5480         continue;
5481
5482       element = Feld[x][y];
5483
5484       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5485       {
5486         element = MovingOrBlocked2Element(x, y);
5487
5488         if (!IS_EXPLOSION_PROOF(element))
5489           RemoveMovingField(x, y);
5490       }
5491
5492       /* indestructible elements can only explode in center (but not flames) */
5493       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5494                                            mode == EX_TYPE_BORDER)) ||
5495           element == EL_FLAMES)
5496         continue;
5497
5498       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5499          behaviour, for example when touching a yamyam that explodes to rocks
5500          with active deadly shield, a rock is created under the player !!! */
5501       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5502 #if 0
5503       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5504           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5505            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5506 #else
5507       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5508 #endif
5509       {
5510         if (IS_ACTIVE_BOMB(element))
5511         {
5512           /* re-activate things under the bomb like gate or penguin */
5513           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5514           Back[x][y] = 0;
5515         }
5516
5517         continue;
5518       }
5519
5520       /* save walkable background elements while explosion on same tile */
5521       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5522           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5523         Back[x][y] = element;
5524
5525       /* ignite explodable elements reached by other explosion */
5526       if (element == EL_EXPLOSION)
5527         element = Store2[x][y];
5528
5529       if (AmoebaNr[x][y] &&
5530           (element == EL_AMOEBA_FULL ||
5531            element == EL_BD_AMOEBA ||
5532            element == EL_AMOEBA_GROWING))
5533       {
5534         AmoebaCnt[AmoebaNr[x][y]]--;
5535         AmoebaCnt2[AmoebaNr[x][y]]--;
5536       }
5537
5538       RemoveField(x, y);
5539
5540       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5541       {
5542         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5543
5544         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5545
5546         if (PLAYERINFO(ex, ey)->use_murphy)
5547           Store[x][y] = EL_EMPTY;
5548       }
5549
5550       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5551          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5552       else if (ELEM_IS_PLAYER(center_element))
5553         Store[x][y] = EL_EMPTY;
5554       else if (center_element == EL_YAMYAM)
5555         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5556       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5557         Store[x][y] = element_info[center_element].content.e[xx][yy];
5558 #if 1
5559       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5560          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5561          otherwise) -- FIX THIS !!! */
5562       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5563         Store[x][y] = element_info[element].content.e[1][1];
5564 #else
5565       else if (!CAN_EXPLODE(element))
5566         Store[x][y] = element_info[element].content.e[1][1];
5567 #endif
5568       else
5569         Store[x][y] = EL_EMPTY;
5570
5571       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5572           center_element == EL_AMOEBA_TO_DIAMOND)
5573         Store2[x][y] = element;
5574
5575       Feld[x][y] = EL_EXPLOSION;
5576       GfxElement[x][y] = artwork_element;
5577
5578       ExplodePhase[x][y] = 1;
5579       ExplodeDelay[x][y] = last_phase;
5580
5581       Stop[x][y] = TRUE;
5582     }
5583
5584     if (center_element == EL_YAMYAM)
5585       game.yamyam_content_nr =
5586         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5587
5588     return;
5589   }
5590
5591   if (Stop[ex][ey])
5592     return;
5593
5594   x = ex;
5595   y = ey;
5596
5597   if (phase == 1)
5598     GfxFrame[x][y] = 0;         /* restart explosion animation */
5599
5600   last_phase = ExplodeDelay[x][y];
5601
5602   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5603
5604   /* this can happen if the player leaves an explosion just in time */
5605   if (GfxElement[x][y] == EL_UNDEFINED)
5606     GfxElement[x][y] = EL_EMPTY;
5607
5608   border_element = Store2[x][y];
5609   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5610     border_element = StorePlayer[x][y];
5611
5612   if (phase == element_info[border_element].ignition_delay ||
5613       phase == last_phase)
5614   {
5615     boolean border_explosion = FALSE;
5616
5617     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5618         !PLAYER_EXPLOSION_PROTECTED(x, y))
5619     {
5620       KillPlayerUnlessExplosionProtected(x, y);
5621       border_explosion = TRUE;
5622     }
5623     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5624     {
5625       Feld[x][y] = Store2[x][y];
5626       Store2[x][y] = 0;
5627       Bang(x, y);
5628       border_explosion = TRUE;
5629     }
5630     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5631     {
5632       AmoebeUmwandeln(x, y);
5633       Store2[x][y] = 0;
5634       border_explosion = TRUE;
5635     }
5636
5637     /* if an element just explodes due to another explosion (chain-reaction),
5638        do not immediately end the new explosion when it was the last frame of
5639        the explosion (as it would be done in the following "if"-statement!) */
5640     if (border_explosion && phase == last_phase)
5641       return;
5642   }
5643
5644   if (phase == last_phase)
5645   {
5646     int element;
5647
5648     element = Feld[x][y] = Store[x][y];
5649     Store[x][y] = Store2[x][y] = 0;
5650     GfxElement[x][y] = EL_UNDEFINED;
5651
5652     /* player can escape from explosions and might therefore be still alive */
5653     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5654         element <= EL_PLAYER_IS_EXPLODING_4)
5655     {
5656       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5657       int explosion_element = EL_PLAYER_1 + player_nr;
5658       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5659       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5660
5661       if (level.use_explosion_element[player_nr])
5662         explosion_element = level.explosion_element[player_nr];
5663
5664       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5665                     element_info[explosion_element].content.e[xx][yy]);
5666     }
5667
5668     /* restore probably existing indestructible background element */
5669     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5670       element = Feld[x][y] = Back[x][y];
5671     Back[x][y] = 0;
5672
5673     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5674     GfxDir[x][y] = MV_NONE;
5675     ChangeDelay[x][y] = 0;
5676     ChangePage[x][y] = -1;
5677
5678     CustomValue[x][y] = 0;
5679
5680     InitField_WithBug2(x, y, FALSE);
5681
5682     TEST_DrawLevelField(x, y);
5683
5684     TestIfElementTouchesCustomElement(x, y);
5685
5686     if (GFX_CRUMBLED(element))
5687       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5688
5689     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5690       StorePlayer[x][y] = 0;
5691
5692     if (ELEM_IS_PLAYER(element))
5693       RelocatePlayer(x, y, element);
5694   }
5695   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5696   {
5697     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5698     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5699
5700     if (phase == delay)
5701       TEST_DrawLevelFieldCrumbled(x, y);
5702
5703     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5704     {
5705       DrawLevelElement(x, y, Back[x][y]);
5706       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5707     }
5708     else if (IS_WALKABLE_UNDER(Back[x][y]))
5709     {
5710       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5711       DrawLevelElementThruMask(x, y, Back[x][y]);
5712     }
5713     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5714       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5715   }
5716 }
5717
5718 void DynaExplode(int ex, int ey)
5719 {
5720   int i, j;
5721   int dynabomb_element = Feld[ex][ey];
5722   int dynabomb_size = 1;
5723   boolean dynabomb_xl = FALSE;
5724   struct PlayerInfo *player;
5725   static int xy[4][2] =
5726   {
5727     { 0, -1 },
5728     { -1, 0 },
5729     { +1, 0 },
5730     { 0, +1 }
5731   };
5732
5733   if (IS_ACTIVE_BOMB(dynabomb_element))
5734   {
5735     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5736     dynabomb_size = player->dynabomb_size;
5737     dynabomb_xl = player->dynabomb_xl;
5738     player->dynabombs_left++;
5739   }
5740
5741   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5742
5743   for (i = 0; i < NUM_DIRECTIONS; i++)
5744   {
5745     for (j = 1; j <= dynabomb_size; j++)
5746     {
5747       int x = ex + j * xy[i][0];
5748       int y = ey + j * xy[i][1];
5749       int element;
5750
5751       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5752         break;
5753
5754       element = Feld[x][y];
5755
5756       /* do not restart explosions of fields with active bombs */
5757       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5758         continue;
5759
5760       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5761
5762       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5763           !IS_DIGGABLE(element) && !dynabomb_xl)
5764         break;
5765     }
5766   }
5767 }
5768
5769 void Bang(int x, int y)
5770 {
5771   int element = MovingOrBlocked2Element(x, y);
5772   int explosion_type = EX_TYPE_NORMAL;
5773
5774   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5775   {
5776     struct PlayerInfo *player = PLAYERINFO(x, y);
5777
5778     element = Feld[x][y] = player->initial_element;
5779
5780     if (level.use_explosion_element[player->index_nr])
5781     {
5782       int explosion_element = level.explosion_element[player->index_nr];
5783
5784       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5785         explosion_type = EX_TYPE_CROSS;
5786       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5787         explosion_type = EX_TYPE_CENTER;
5788     }
5789   }
5790
5791   switch (element)
5792   {
5793     case EL_BUG:
5794     case EL_SPACESHIP:
5795     case EL_BD_BUTTERFLY:
5796     case EL_BD_FIREFLY:
5797     case EL_YAMYAM:
5798     case EL_DARK_YAMYAM:
5799     case EL_ROBOT:
5800     case EL_PACMAN:
5801     case EL_MOLE:
5802       RaiseScoreElement(element);
5803       break;
5804
5805     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5806     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5807     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5808     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5809     case EL_DYNABOMB_INCREASE_NUMBER:
5810     case EL_DYNABOMB_INCREASE_SIZE:
5811     case EL_DYNABOMB_INCREASE_POWER:
5812       explosion_type = EX_TYPE_DYNA;
5813       break;
5814
5815     case EL_DC_LANDMINE:
5816       explosion_type = EX_TYPE_CENTER;
5817       break;
5818
5819     case EL_PENGUIN:
5820     case EL_LAMP:
5821     case EL_LAMP_ACTIVE:
5822     case EL_AMOEBA_TO_DIAMOND:
5823       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5824         explosion_type = EX_TYPE_CENTER;
5825       break;
5826
5827     default:
5828       if (element_info[element].explosion_type == EXPLODES_CROSS)
5829         explosion_type = EX_TYPE_CROSS;
5830       else if (element_info[element].explosion_type == EXPLODES_1X1)
5831         explosion_type = EX_TYPE_CENTER;
5832       break;
5833   }
5834
5835   if (explosion_type == EX_TYPE_DYNA)
5836     DynaExplode(x, y);
5837   else
5838     Explode(x, y, EX_PHASE_START, explosion_type);
5839
5840   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5841 }
5842
5843 void SplashAcid(int x, int y)
5844 {
5845   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5846       (!IN_LEV_FIELD(x - 1, y - 2) ||
5847        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5848     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5849
5850   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5851       (!IN_LEV_FIELD(x + 1, y - 2) ||
5852        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5853     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5854
5855   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5856 }
5857
5858 static void InitBeltMovement()
5859 {
5860   static int belt_base_element[4] =
5861   {
5862     EL_CONVEYOR_BELT_1_LEFT,
5863     EL_CONVEYOR_BELT_2_LEFT,
5864     EL_CONVEYOR_BELT_3_LEFT,
5865     EL_CONVEYOR_BELT_4_LEFT
5866   };
5867   static int belt_base_active_element[4] =
5868   {
5869     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5870     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5871     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5872     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5873   };
5874
5875   int x, y, i, j;
5876
5877   /* set frame order for belt animation graphic according to belt direction */
5878   for (i = 0; i < NUM_BELTS; i++)
5879   {
5880     int belt_nr = i;
5881
5882     for (j = 0; j < NUM_BELT_PARTS; j++)
5883     {
5884       int element = belt_base_active_element[belt_nr] + j;
5885       int graphic_1 = el2img(element);
5886       int graphic_2 = el2panelimg(element);
5887
5888       if (game.belt_dir[i] == MV_LEFT)
5889       {
5890         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5891         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5892       }
5893       else
5894       {
5895         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5896         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5897       }
5898     }
5899   }
5900
5901   SCAN_PLAYFIELD(x, y)
5902   {
5903     int element = Feld[x][y];
5904
5905     for (i = 0; i < NUM_BELTS; i++)
5906     {
5907       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5908       {
5909         int e_belt_nr = getBeltNrFromBeltElement(element);
5910         int belt_nr = i;
5911
5912         if (e_belt_nr == belt_nr)
5913         {
5914           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5915
5916           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5917         }
5918       }
5919     }
5920   }
5921 }
5922
5923 static void ToggleBeltSwitch(int x, int y)
5924 {
5925   static int belt_base_element[4] =
5926   {
5927     EL_CONVEYOR_BELT_1_LEFT,
5928     EL_CONVEYOR_BELT_2_LEFT,
5929     EL_CONVEYOR_BELT_3_LEFT,
5930     EL_CONVEYOR_BELT_4_LEFT
5931   };
5932   static int belt_base_active_element[4] =
5933   {
5934     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5935     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5936     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5937     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5938   };
5939   static int belt_base_switch_element[4] =
5940   {
5941     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5942     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5943     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5944     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5945   };
5946   static int belt_move_dir[4] =
5947   {
5948     MV_LEFT,
5949     MV_NONE,
5950     MV_RIGHT,
5951     MV_NONE,
5952   };
5953
5954   int element = Feld[x][y];
5955   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5956   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5957   int belt_dir = belt_move_dir[belt_dir_nr];
5958   int xx, yy, i;
5959
5960   if (!IS_BELT_SWITCH(element))
5961     return;
5962
5963   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5964   game.belt_dir[belt_nr] = belt_dir;
5965
5966   if (belt_dir_nr == 3)
5967     belt_dir_nr = 1;
5968
5969   /* set frame order for belt animation graphic according to belt direction */
5970   for (i = 0; i < NUM_BELT_PARTS; i++)
5971   {
5972     int element = belt_base_active_element[belt_nr] + i;
5973     int graphic_1 = el2img(element);
5974     int graphic_2 = el2panelimg(element);
5975
5976     if (belt_dir == MV_LEFT)
5977     {
5978       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5979       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5980     }
5981     else
5982     {
5983       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5984       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5985     }
5986   }
5987
5988   SCAN_PLAYFIELD(xx, yy)
5989   {
5990     int element = Feld[xx][yy];
5991
5992     if (IS_BELT_SWITCH(element))
5993     {
5994       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5995
5996       if (e_belt_nr == belt_nr)
5997       {
5998         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5999         TEST_DrawLevelField(xx, yy);
6000       }
6001     }
6002     else if (IS_BELT(element) && belt_dir != MV_NONE)
6003     {
6004       int e_belt_nr = getBeltNrFromBeltElement(element);
6005
6006       if (e_belt_nr == belt_nr)
6007       {
6008         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6009
6010         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6011         TEST_DrawLevelField(xx, yy);
6012       }
6013     }
6014     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6015     {
6016       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6017
6018       if (e_belt_nr == belt_nr)
6019       {
6020         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6021
6022         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6023         TEST_DrawLevelField(xx, yy);
6024       }
6025     }
6026   }
6027 }
6028
6029 static void ToggleSwitchgateSwitch(int x, int y)
6030 {
6031   int xx, yy;
6032
6033   game.switchgate_pos = !game.switchgate_pos;
6034
6035   SCAN_PLAYFIELD(xx, yy)
6036   {
6037     int element = Feld[xx][yy];
6038
6039     if (element == EL_SWITCHGATE_SWITCH_UP)
6040     {
6041       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6042       TEST_DrawLevelField(xx, yy);
6043     }
6044     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6045     {
6046       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6047       TEST_DrawLevelField(xx, yy);
6048     }
6049     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6050     {
6051       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6052       TEST_DrawLevelField(xx, yy);
6053     }
6054     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6055     {
6056       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6057       TEST_DrawLevelField(xx, yy);
6058     }
6059     else if (element == EL_SWITCHGATE_OPEN ||
6060              element == EL_SWITCHGATE_OPENING)
6061     {
6062       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6063
6064       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6065     }
6066     else if (element == EL_SWITCHGATE_CLOSED ||
6067              element == EL_SWITCHGATE_CLOSING)
6068     {
6069       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6070
6071       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6072     }
6073   }
6074 }
6075
6076 static int getInvisibleActiveFromInvisibleElement(int element)
6077 {
6078   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6079           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6080           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6081           element);
6082 }
6083
6084 static int getInvisibleFromInvisibleActiveElement(int element)
6085 {
6086   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6087           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6088           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6089           element);
6090 }
6091
6092 static void RedrawAllLightSwitchesAndInvisibleElements()
6093 {
6094   int x, y;
6095
6096   SCAN_PLAYFIELD(x, y)
6097   {
6098     int element = Feld[x][y];
6099
6100     if (element == EL_LIGHT_SWITCH &&
6101         game.light_time_left > 0)
6102     {
6103       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6104       TEST_DrawLevelField(x, y);
6105     }
6106     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6107              game.light_time_left == 0)
6108     {
6109       Feld[x][y] = EL_LIGHT_SWITCH;
6110       TEST_DrawLevelField(x, y);
6111     }
6112     else if (element == EL_EMC_DRIPPER &&
6113              game.light_time_left > 0)
6114     {
6115       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6116       TEST_DrawLevelField(x, y);
6117     }
6118     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6119              game.light_time_left == 0)
6120     {
6121       Feld[x][y] = EL_EMC_DRIPPER;
6122       TEST_DrawLevelField(x, y);
6123     }
6124     else if (element == EL_INVISIBLE_STEELWALL ||
6125              element == EL_INVISIBLE_WALL ||
6126              element == EL_INVISIBLE_SAND)
6127     {
6128       if (game.light_time_left > 0)
6129         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6130
6131       TEST_DrawLevelField(x, y);
6132
6133       /* uncrumble neighbour fields, if needed */
6134       if (element == EL_INVISIBLE_SAND)
6135         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6136     }
6137     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6138              element == EL_INVISIBLE_WALL_ACTIVE ||
6139              element == EL_INVISIBLE_SAND_ACTIVE)
6140     {
6141       if (game.light_time_left == 0)
6142         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6143
6144       TEST_DrawLevelField(x, y);
6145
6146       /* re-crumble neighbour fields, if needed */
6147       if (element == EL_INVISIBLE_SAND)
6148         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6149     }
6150   }
6151 }
6152
6153 static void RedrawAllInvisibleElementsForLenses()
6154 {
6155   int x, y;
6156
6157   SCAN_PLAYFIELD(x, y)
6158   {
6159     int element = Feld[x][y];
6160
6161     if (element == EL_EMC_DRIPPER &&
6162         game.lenses_time_left > 0)
6163     {
6164       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6165       TEST_DrawLevelField(x, y);
6166     }
6167     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6168              game.lenses_time_left == 0)
6169     {
6170       Feld[x][y] = EL_EMC_DRIPPER;
6171       TEST_DrawLevelField(x, y);
6172     }
6173     else if (element == EL_INVISIBLE_STEELWALL ||
6174              element == EL_INVISIBLE_WALL ||
6175              element == EL_INVISIBLE_SAND)
6176     {
6177       if (game.lenses_time_left > 0)
6178         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6179
6180       TEST_DrawLevelField(x, y);
6181
6182       /* uncrumble neighbour fields, if needed */
6183       if (element == EL_INVISIBLE_SAND)
6184         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6185     }
6186     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6187              element == EL_INVISIBLE_WALL_ACTIVE ||
6188              element == EL_INVISIBLE_SAND_ACTIVE)
6189     {
6190       if (game.lenses_time_left == 0)
6191         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6192
6193       TEST_DrawLevelField(x, y);
6194
6195       /* re-crumble neighbour fields, if needed */
6196       if (element == EL_INVISIBLE_SAND)
6197         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6198     }
6199   }
6200 }
6201
6202 static void RedrawAllInvisibleElementsForMagnifier()
6203 {
6204   int x, y;
6205
6206   SCAN_PLAYFIELD(x, y)
6207   {
6208     int element = Feld[x][y];
6209
6210     if (element == EL_EMC_FAKE_GRASS &&
6211         game.magnify_time_left > 0)
6212     {
6213       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6214       TEST_DrawLevelField(x, y);
6215     }
6216     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6217              game.magnify_time_left == 0)
6218     {
6219       Feld[x][y] = EL_EMC_FAKE_GRASS;
6220       TEST_DrawLevelField(x, y);
6221     }
6222     else if (IS_GATE_GRAY(element) &&
6223              game.magnify_time_left > 0)
6224     {
6225       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6226                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6227                     IS_EM_GATE_GRAY(element) ?
6228                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6229                     IS_EMC_GATE_GRAY(element) ?
6230                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6231                     IS_DC_GATE_GRAY(element) ?
6232                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6233                     element);
6234       TEST_DrawLevelField(x, y);
6235     }
6236     else if (IS_GATE_GRAY_ACTIVE(element) &&
6237              game.magnify_time_left == 0)
6238     {
6239       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6240                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6241                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6242                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6243                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6244                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6245                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6246                     EL_DC_GATE_WHITE_GRAY :
6247                     element);
6248       TEST_DrawLevelField(x, y);
6249     }
6250   }
6251 }
6252
6253 static void ToggleLightSwitch(int x, int y)
6254 {
6255   int element = Feld[x][y];
6256
6257   game.light_time_left =
6258     (element == EL_LIGHT_SWITCH ?
6259      level.time_light * FRAMES_PER_SECOND : 0);
6260
6261   RedrawAllLightSwitchesAndInvisibleElements();
6262 }
6263
6264 static void ActivateTimegateSwitch(int x, int y)
6265 {
6266   int xx, yy;
6267
6268   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6269
6270   SCAN_PLAYFIELD(xx, yy)
6271   {
6272     int element = Feld[xx][yy];
6273
6274     if (element == EL_TIMEGATE_CLOSED ||
6275         element == EL_TIMEGATE_CLOSING)
6276     {
6277       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6278       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6279     }
6280
6281     /*
6282     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6283     {
6284       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6285       TEST_DrawLevelField(xx, yy);
6286     }
6287     */
6288
6289   }
6290
6291   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6292                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6293 }
6294
6295 void Impact(int x, int y)
6296 {
6297   boolean last_line = (y == lev_fieldy - 1);
6298   boolean object_hit = FALSE;
6299   boolean impact = (last_line || object_hit);
6300   int element = Feld[x][y];
6301   int smashed = EL_STEELWALL;
6302
6303   if (!last_line)       /* check if element below was hit */
6304   {
6305     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6306       return;
6307
6308     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6309                                          MovDir[x][y + 1] != MV_DOWN ||
6310                                          MovPos[x][y + 1] <= TILEY / 2));
6311
6312     /* do not smash moving elements that left the smashed field in time */
6313     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6314         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6315       object_hit = FALSE;
6316
6317 #if USE_QUICKSAND_IMPACT_BUGFIX
6318     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6319     {
6320       RemoveMovingField(x, y + 1);
6321       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6322       Feld[x][y + 2] = EL_ROCK;
6323       TEST_DrawLevelField(x, y + 2);
6324
6325       object_hit = TRUE;
6326     }
6327
6328     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6329     {
6330       RemoveMovingField(x, y + 1);
6331       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6332       Feld[x][y + 2] = EL_ROCK;
6333       TEST_DrawLevelField(x, y + 2);
6334
6335       object_hit = TRUE;
6336     }
6337 #endif
6338
6339     if (object_hit)
6340       smashed = MovingOrBlocked2Element(x, y + 1);
6341
6342     impact = (last_line || object_hit);
6343   }
6344
6345   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6346   {
6347     SplashAcid(x, y + 1);
6348     return;
6349   }
6350
6351   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6352   /* only reset graphic animation if graphic really changes after impact */
6353   if (impact &&
6354       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6355   {
6356     ResetGfxAnimation(x, y);
6357     TEST_DrawLevelField(x, y);
6358   }
6359
6360   if (impact && CAN_EXPLODE_IMPACT(element))
6361   {
6362     Bang(x, y);
6363     return;
6364   }
6365   else if (impact && element == EL_PEARL &&
6366            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6367   {
6368     ResetGfxAnimation(x, y);
6369
6370     Feld[x][y] = EL_PEARL_BREAKING;
6371     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6372     return;
6373   }
6374   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6375   {
6376     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6377
6378     return;
6379   }
6380
6381   if (impact && element == EL_AMOEBA_DROP)
6382   {
6383     if (object_hit && IS_PLAYER(x, y + 1))
6384       KillPlayerUnlessEnemyProtected(x, y + 1);
6385     else if (object_hit && smashed == EL_PENGUIN)
6386       Bang(x, y + 1);
6387     else
6388     {
6389       Feld[x][y] = EL_AMOEBA_GROWING;
6390       Store[x][y] = EL_AMOEBA_WET;
6391
6392       ResetRandomAnimationValue(x, y);
6393     }
6394     return;
6395   }
6396
6397   if (object_hit)               /* check which object was hit */
6398   {
6399     if ((CAN_PASS_MAGIC_WALL(element) && 
6400          (smashed == EL_MAGIC_WALL ||
6401           smashed == EL_BD_MAGIC_WALL)) ||
6402         (CAN_PASS_DC_MAGIC_WALL(element) &&
6403          smashed == EL_DC_MAGIC_WALL))
6404     {
6405       int xx, yy;
6406       int activated_magic_wall =
6407         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6408          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6409          EL_DC_MAGIC_WALL_ACTIVE);
6410
6411       /* activate magic wall / mill */
6412       SCAN_PLAYFIELD(xx, yy)
6413       {
6414         if (Feld[xx][yy] == smashed)
6415           Feld[xx][yy] = activated_magic_wall;
6416       }
6417
6418       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6419       game.magic_wall_active = TRUE;
6420
6421       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6422                             SND_MAGIC_WALL_ACTIVATING :
6423                             smashed == EL_BD_MAGIC_WALL ?
6424                             SND_BD_MAGIC_WALL_ACTIVATING :
6425                             SND_DC_MAGIC_WALL_ACTIVATING));
6426     }
6427
6428     if (IS_PLAYER(x, y + 1))
6429     {
6430       if (CAN_SMASH_PLAYER(element))
6431       {
6432         KillPlayerUnlessEnemyProtected(x, y + 1);
6433         return;
6434       }
6435     }
6436     else if (smashed == EL_PENGUIN)
6437     {
6438       if (CAN_SMASH_PLAYER(element))
6439       {
6440         Bang(x, y + 1);
6441         return;
6442       }
6443     }
6444     else if (element == EL_BD_DIAMOND)
6445     {
6446       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6447       {
6448         Bang(x, y + 1);
6449         return;
6450       }
6451     }
6452     else if (((element == EL_SP_INFOTRON ||
6453                element == EL_SP_ZONK) &&
6454               (smashed == EL_SP_SNIKSNAK ||
6455                smashed == EL_SP_ELECTRON ||
6456                smashed == EL_SP_DISK_ORANGE)) ||
6457              (element == EL_SP_INFOTRON &&
6458               smashed == EL_SP_DISK_YELLOW))
6459     {
6460       Bang(x, y + 1);
6461       return;
6462     }
6463     else if (CAN_SMASH_EVERYTHING(element))
6464     {
6465       if (IS_CLASSIC_ENEMY(smashed) ||
6466           CAN_EXPLODE_SMASHED(smashed))
6467       {
6468         Bang(x, y + 1);
6469         return;
6470       }
6471       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6472       {
6473         if (smashed == EL_LAMP ||
6474             smashed == EL_LAMP_ACTIVE)
6475         {
6476           Bang(x, y + 1);
6477           return;
6478         }
6479         else if (smashed == EL_NUT)
6480         {
6481           Feld[x][y + 1] = EL_NUT_BREAKING;
6482           PlayLevelSound(x, y, SND_NUT_BREAKING);
6483           RaiseScoreElement(EL_NUT);
6484           return;
6485         }
6486         else if (smashed == EL_PEARL)
6487         {
6488           ResetGfxAnimation(x, y);
6489
6490           Feld[x][y + 1] = EL_PEARL_BREAKING;
6491           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6492           return;
6493         }
6494         else if (smashed == EL_DIAMOND)
6495         {
6496           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6497           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6498           return;
6499         }
6500         else if (IS_BELT_SWITCH(smashed))
6501         {
6502           ToggleBeltSwitch(x, y + 1);
6503         }
6504         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6505                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6506                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6507                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6508         {
6509           ToggleSwitchgateSwitch(x, y + 1);
6510         }
6511         else if (smashed == EL_LIGHT_SWITCH ||
6512                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6513         {
6514           ToggleLightSwitch(x, y + 1);
6515         }
6516         else
6517         {
6518           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6519
6520           CheckElementChangeBySide(x, y + 1, smashed, element,
6521                                    CE_SWITCHED, CH_SIDE_TOP);
6522           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6523                                             CH_SIDE_TOP);
6524         }
6525       }
6526       else
6527       {
6528         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6529       }
6530     }
6531   }
6532
6533   /* play sound of magic wall / mill */
6534   if (!last_line &&
6535       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6536        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6537        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6538   {
6539     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6540       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6541     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6542       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6543     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6544       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6545
6546     return;
6547   }
6548
6549   /* play sound of object that hits the ground */
6550   if (last_line || object_hit)
6551     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6552 }
6553
6554 inline static void TurnRoundExt(int x, int y)
6555 {
6556   static struct
6557   {
6558     int dx, dy;
6559   } move_xy[] =
6560   {
6561     {  0,  0 },
6562     { -1,  0 },
6563     { +1,  0 },
6564     {  0,  0 },
6565     {  0, -1 },
6566     {  0,  0 }, { 0, 0 }, { 0, 0 },
6567     {  0, +1 }
6568   };
6569   static struct
6570   {
6571     int left, right, back;
6572   } turn[] =
6573   {
6574     { 0,        0,              0        },
6575     { MV_DOWN,  MV_UP,          MV_RIGHT },
6576     { MV_UP,    MV_DOWN,        MV_LEFT  },
6577     { 0,        0,              0        },
6578     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6579     { 0,        0,              0        },
6580     { 0,        0,              0        },
6581     { 0,        0,              0        },
6582     { MV_RIGHT, MV_LEFT,        MV_UP    }
6583   };
6584
6585   int element = Feld[x][y];
6586   int move_pattern = element_info[element].move_pattern;
6587
6588   int old_move_dir = MovDir[x][y];
6589   int left_dir  = turn[old_move_dir].left;
6590   int right_dir = turn[old_move_dir].right;
6591   int back_dir  = turn[old_move_dir].back;
6592
6593   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6594   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6595   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6596   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6597
6598   int left_x  = x + left_dx,  left_y  = y + left_dy;
6599   int right_x = x + right_dx, right_y = y + right_dy;
6600   int move_x  = x + move_dx,  move_y  = y + move_dy;
6601
6602   int xx, yy;
6603
6604   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6605   {
6606     TestIfBadThingTouchesOtherBadThing(x, y);
6607
6608     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6609       MovDir[x][y] = right_dir;
6610     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6611       MovDir[x][y] = left_dir;
6612
6613     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6614       MovDelay[x][y] = 9;
6615     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6616       MovDelay[x][y] = 1;
6617   }
6618   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6619   {
6620     TestIfBadThingTouchesOtherBadThing(x, y);
6621
6622     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6623       MovDir[x][y] = left_dir;
6624     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6625       MovDir[x][y] = right_dir;
6626
6627     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6628       MovDelay[x][y] = 9;
6629     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6630       MovDelay[x][y] = 1;
6631   }
6632   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6633   {
6634     TestIfBadThingTouchesOtherBadThing(x, y);
6635
6636     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6637       MovDir[x][y] = left_dir;
6638     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6639       MovDir[x][y] = right_dir;
6640
6641     if (MovDir[x][y] != old_move_dir)
6642       MovDelay[x][y] = 9;
6643   }
6644   else if (element == EL_YAMYAM)
6645   {
6646     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6647     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6648
6649     if (can_turn_left && can_turn_right)
6650       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6651     else if (can_turn_left)
6652       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6653     else if (can_turn_right)
6654       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6655     else
6656       MovDir[x][y] = back_dir;
6657
6658     MovDelay[x][y] = 16 + 16 * RND(3);
6659   }
6660   else if (element == EL_DARK_YAMYAM)
6661   {
6662     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6663                                                          left_x, left_y);
6664     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6665                                                          right_x, right_y);
6666
6667     if (can_turn_left && can_turn_right)
6668       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6669     else if (can_turn_left)
6670       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6671     else if (can_turn_right)
6672       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6673     else
6674       MovDir[x][y] = back_dir;
6675
6676     MovDelay[x][y] = 16 + 16 * RND(3);
6677   }
6678   else if (element == EL_PACMAN)
6679   {
6680     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6681     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6682
6683     if (can_turn_left && can_turn_right)
6684       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6685     else if (can_turn_left)
6686       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6687     else if (can_turn_right)
6688       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6689     else
6690       MovDir[x][y] = back_dir;
6691
6692     MovDelay[x][y] = 6 + RND(40);
6693   }
6694   else if (element == EL_PIG)
6695   {
6696     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6697     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6698     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6699     boolean should_turn_left, should_turn_right, should_move_on;
6700     int rnd_value = 24;
6701     int rnd = RND(rnd_value);
6702
6703     should_turn_left = (can_turn_left &&
6704                         (!can_move_on ||
6705                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6706                                                    y + back_dy + left_dy)));
6707     should_turn_right = (can_turn_right &&
6708                          (!can_move_on ||
6709                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6710                                                     y + back_dy + right_dy)));
6711     should_move_on = (can_move_on &&
6712                       (!can_turn_left ||
6713                        !can_turn_right ||
6714                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6715                                                  y + move_dy + left_dy) ||
6716                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6717                                                  y + move_dy + right_dy)));
6718
6719     if (should_turn_left || should_turn_right || should_move_on)
6720     {
6721       if (should_turn_left && should_turn_right && should_move_on)
6722         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6723                         rnd < 2 * rnd_value / 3 ? right_dir :
6724                         old_move_dir);
6725       else if (should_turn_left && should_turn_right)
6726         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6727       else if (should_turn_left && should_move_on)
6728         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6729       else if (should_turn_right && should_move_on)
6730         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6731       else if (should_turn_left)
6732         MovDir[x][y] = left_dir;
6733       else if (should_turn_right)
6734         MovDir[x][y] = right_dir;
6735       else if (should_move_on)
6736         MovDir[x][y] = old_move_dir;
6737     }
6738     else if (can_move_on && rnd > rnd_value / 8)
6739       MovDir[x][y] = old_move_dir;
6740     else if (can_turn_left && can_turn_right)
6741       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6742     else if (can_turn_left && rnd > rnd_value / 8)
6743       MovDir[x][y] = left_dir;
6744     else if (can_turn_right && rnd > rnd_value/8)
6745       MovDir[x][y] = right_dir;
6746     else
6747       MovDir[x][y] = back_dir;
6748
6749     xx = x + move_xy[MovDir[x][y]].dx;
6750     yy = y + move_xy[MovDir[x][y]].dy;
6751
6752     if (!IN_LEV_FIELD(xx, yy) ||
6753         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6754       MovDir[x][y] = old_move_dir;
6755
6756     MovDelay[x][y] = 0;
6757   }
6758   else if (element == EL_DRAGON)
6759   {
6760     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6761     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6762     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6763     int rnd_value = 24;
6764     int rnd = RND(rnd_value);
6765
6766     if (can_move_on && rnd > rnd_value / 8)
6767       MovDir[x][y] = old_move_dir;
6768     else if (can_turn_left && can_turn_right)
6769       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6770     else if (can_turn_left && rnd > rnd_value / 8)
6771       MovDir[x][y] = left_dir;
6772     else if (can_turn_right && rnd > rnd_value / 8)
6773       MovDir[x][y] = right_dir;
6774     else
6775       MovDir[x][y] = back_dir;
6776
6777     xx = x + move_xy[MovDir[x][y]].dx;
6778     yy = y + move_xy[MovDir[x][y]].dy;
6779
6780     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6781       MovDir[x][y] = old_move_dir;
6782
6783     MovDelay[x][y] = 0;
6784   }
6785   else if (element == EL_MOLE)
6786   {
6787     boolean can_move_on =
6788       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6789                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6790                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6791     if (!can_move_on)
6792     {
6793       boolean can_turn_left =
6794         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6795                               IS_AMOEBOID(Feld[left_x][left_y])));
6796
6797       boolean can_turn_right =
6798         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6799                               IS_AMOEBOID(Feld[right_x][right_y])));
6800
6801       if (can_turn_left && can_turn_right)
6802         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6803       else if (can_turn_left)
6804         MovDir[x][y] = left_dir;
6805       else
6806         MovDir[x][y] = right_dir;
6807     }
6808
6809     if (MovDir[x][y] != old_move_dir)
6810       MovDelay[x][y] = 9;
6811   }
6812   else if (element == EL_BALLOON)
6813   {
6814     MovDir[x][y] = game.wind_direction;
6815     MovDelay[x][y] = 0;
6816   }
6817   else if (element == EL_SPRING)
6818   {
6819     if (MovDir[x][y] & MV_HORIZONTAL)
6820     {
6821       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6822           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6823       {
6824         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6825         ResetGfxAnimation(move_x, move_y);
6826         TEST_DrawLevelField(move_x, move_y);
6827
6828         MovDir[x][y] = back_dir;
6829       }
6830       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6831                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6832         MovDir[x][y] = MV_NONE;
6833     }
6834
6835     MovDelay[x][y] = 0;
6836   }
6837   else if (element == EL_ROBOT ||
6838            element == EL_SATELLITE ||
6839            element == EL_PENGUIN ||
6840            element == EL_EMC_ANDROID)
6841   {
6842     int attr_x = -1, attr_y = -1;
6843
6844     if (AllPlayersGone)
6845     {
6846       attr_x = ExitX;
6847       attr_y = ExitY;
6848     }
6849     else
6850     {
6851       int i;
6852
6853       for (i = 0; i < MAX_PLAYERS; i++)
6854       {
6855         struct PlayerInfo *player = &stored_player[i];
6856         int jx = player->jx, jy = player->jy;
6857
6858         if (!player->active)
6859           continue;
6860
6861         if (attr_x == -1 ||
6862             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6863         {
6864           attr_x = jx;
6865           attr_y = jy;
6866         }
6867       }
6868     }
6869
6870     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6871         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6872          game.engine_version < VERSION_IDENT(3,1,0,0)))
6873     {
6874       attr_x = ZX;
6875       attr_y = ZY;
6876     }
6877
6878     if (element == EL_PENGUIN)
6879     {
6880       int i;
6881       static int xy[4][2] =
6882       {
6883         { 0, -1 },
6884         { -1, 0 },
6885         { +1, 0 },
6886         { 0, +1 }
6887       };
6888
6889       for (i = 0; i < NUM_DIRECTIONS; i++)
6890       {
6891         int ex = x + xy[i][0];
6892         int ey = y + xy[i][1];
6893
6894         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6895                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6896                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6897                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6898         {
6899           attr_x = ex;
6900           attr_y = ey;
6901           break;
6902         }
6903       }
6904     }
6905
6906     MovDir[x][y] = MV_NONE;
6907     if (attr_x < x)
6908       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6909     else if (attr_x > x)
6910       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6911     if (attr_y < y)
6912       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6913     else if (attr_y > y)
6914       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6915
6916     if (element == EL_ROBOT)
6917     {
6918       int newx, newy;
6919
6920       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6921         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6922       Moving2Blocked(x, y, &newx, &newy);
6923
6924       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6925         MovDelay[x][y] = 8 + 8 * !RND(3);
6926       else
6927         MovDelay[x][y] = 16;
6928     }
6929     else if (element == EL_PENGUIN)
6930     {
6931       int newx, newy;
6932
6933       MovDelay[x][y] = 1;
6934
6935       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6936       {
6937         boolean first_horiz = RND(2);
6938         int new_move_dir = MovDir[x][y];
6939
6940         MovDir[x][y] =
6941           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6942         Moving2Blocked(x, y, &newx, &newy);
6943
6944         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6945           return;
6946
6947         MovDir[x][y] =
6948           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6949         Moving2Blocked(x, y, &newx, &newy);
6950
6951         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6952           return;
6953
6954         MovDir[x][y] = old_move_dir;
6955         return;
6956       }
6957     }
6958     else if (element == EL_SATELLITE)
6959     {
6960       int newx, newy;
6961
6962       MovDelay[x][y] = 1;
6963
6964       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6965       {
6966         boolean first_horiz = RND(2);
6967         int new_move_dir = MovDir[x][y];
6968
6969         MovDir[x][y] =
6970           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6971         Moving2Blocked(x, y, &newx, &newy);
6972
6973         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6974           return;
6975
6976         MovDir[x][y] =
6977           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6978         Moving2Blocked(x, y, &newx, &newy);
6979
6980         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6981           return;
6982
6983         MovDir[x][y] = old_move_dir;
6984         return;
6985       }
6986     }
6987     else if (element == EL_EMC_ANDROID)
6988     {
6989       static int check_pos[16] =
6990       {
6991         -1,             /*  0 => (invalid)          */
6992         7,              /*  1 => MV_LEFT            */
6993         3,              /*  2 => MV_RIGHT           */
6994         -1,             /*  3 => (invalid)          */
6995         1,              /*  4 =>            MV_UP   */
6996         0,              /*  5 => MV_LEFT  | MV_UP   */
6997         2,              /*  6 => MV_RIGHT | MV_UP   */
6998         -1,             /*  7 => (invalid)          */
6999         5,              /*  8 =>            MV_DOWN */
7000         6,              /*  9 => MV_LEFT  | MV_DOWN */
7001         4,              /* 10 => MV_RIGHT | MV_DOWN */
7002         -1,             /* 11 => (invalid)          */
7003         -1,             /* 12 => (invalid)          */
7004         -1,             /* 13 => (invalid)          */
7005         -1,             /* 14 => (invalid)          */
7006         -1,             /* 15 => (invalid)          */
7007       };
7008       static struct
7009       {
7010         int dx, dy;
7011         int dir;
7012       } check_xy[8] =
7013       {
7014         { -1, -1,       MV_LEFT  | MV_UP   },
7015         {  0, -1,                  MV_UP   },
7016         { +1, -1,       MV_RIGHT | MV_UP   },
7017         { +1,  0,       MV_RIGHT           },
7018         { +1, +1,       MV_RIGHT | MV_DOWN },
7019         {  0, +1,                  MV_DOWN },
7020         { -1, +1,       MV_LEFT  | MV_DOWN },
7021         { -1,  0,       MV_LEFT            },
7022       };
7023       int start_pos, check_order;
7024       boolean can_clone = FALSE;
7025       int i;
7026
7027       /* check if there is any free field around current position */
7028       for (i = 0; i < 8; i++)
7029       {
7030         int newx = x + check_xy[i].dx;
7031         int newy = y + check_xy[i].dy;
7032
7033         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7034         {
7035           can_clone = TRUE;
7036
7037           break;
7038         }
7039       }
7040
7041       if (can_clone)            /* randomly find an element to clone */
7042       {
7043         can_clone = FALSE;
7044
7045         start_pos = check_pos[RND(8)];
7046         check_order = (RND(2) ? -1 : +1);
7047
7048         for (i = 0; i < 8; i++)
7049         {
7050           int pos_raw = start_pos + i * check_order;
7051           int pos = (pos_raw + 8) % 8;
7052           int newx = x + check_xy[pos].dx;
7053           int newy = y + check_xy[pos].dy;
7054
7055           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7056           {
7057             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7058             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7059
7060             Store[x][y] = Feld[newx][newy];
7061
7062             can_clone = TRUE;
7063
7064             break;
7065           }
7066         }
7067       }
7068
7069       if (can_clone)            /* randomly find a direction to move */
7070       {
7071         can_clone = FALSE;
7072
7073         start_pos = check_pos[RND(8)];
7074         check_order = (RND(2) ? -1 : +1);
7075
7076         for (i = 0; i < 8; i++)
7077         {
7078           int pos_raw = start_pos + i * check_order;
7079           int pos = (pos_raw + 8) % 8;
7080           int newx = x + check_xy[pos].dx;
7081           int newy = y + check_xy[pos].dy;
7082           int new_move_dir = check_xy[pos].dir;
7083
7084           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7085           {
7086             MovDir[x][y] = new_move_dir;
7087             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7088
7089             can_clone = TRUE;
7090
7091             break;
7092           }
7093         }
7094       }
7095
7096       if (can_clone)            /* cloning and moving successful */
7097         return;
7098
7099       /* cannot clone -- try to move towards player */
7100
7101       start_pos = check_pos[MovDir[x][y] & 0x0f];
7102       check_order = (RND(2) ? -1 : +1);
7103
7104       for (i = 0; i < 3; i++)
7105       {
7106         /* first check start_pos, then previous/next or (next/previous) pos */
7107         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7108         int pos = (pos_raw + 8) % 8;
7109         int newx = x + check_xy[pos].dx;
7110         int newy = y + check_xy[pos].dy;
7111         int new_move_dir = check_xy[pos].dir;
7112
7113         if (IS_PLAYER(newx, newy))
7114           break;
7115
7116         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7117         {
7118           MovDir[x][y] = new_move_dir;
7119           MovDelay[x][y] = level.android_move_time * 8 + 1;
7120
7121           break;
7122         }
7123       }
7124     }
7125   }
7126   else if (move_pattern == MV_TURNING_LEFT ||
7127            move_pattern == MV_TURNING_RIGHT ||
7128            move_pattern == MV_TURNING_LEFT_RIGHT ||
7129            move_pattern == MV_TURNING_RIGHT_LEFT ||
7130            move_pattern == MV_TURNING_RANDOM ||
7131            move_pattern == MV_ALL_DIRECTIONS)
7132   {
7133     boolean can_turn_left =
7134       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7135     boolean can_turn_right =
7136       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7137
7138     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7139       return;
7140
7141     if (move_pattern == MV_TURNING_LEFT)
7142       MovDir[x][y] = left_dir;
7143     else if (move_pattern == MV_TURNING_RIGHT)
7144       MovDir[x][y] = right_dir;
7145     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7146       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7147     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7148       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7149     else if (move_pattern == MV_TURNING_RANDOM)
7150       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7151                       can_turn_right && !can_turn_left ? right_dir :
7152                       RND(2) ? left_dir : right_dir);
7153     else if (can_turn_left && can_turn_right)
7154       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7155     else if (can_turn_left)
7156       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7157     else if (can_turn_right)
7158       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7159     else
7160       MovDir[x][y] = back_dir;
7161
7162     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7163   }
7164   else if (move_pattern == MV_HORIZONTAL ||
7165            move_pattern == MV_VERTICAL)
7166   {
7167     if (move_pattern & old_move_dir)
7168       MovDir[x][y] = back_dir;
7169     else if (move_pattern == MV_HORIZONTAL)
7170       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7171     else if (move_pattern == MV_VERTICAL)
7172       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7173
7174     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7175   }
7176   else if (move_pattern & MV_ANY_DIRECTION)
7177   {
7178     MovDir[x][y] = move_pattern;
7179     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7180   }
7181   else if (move_pattern & MV_WIND_DIRECTION)
7182   {
7183     MovDir[x][y] = game.wind_direction;
7184     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7185   }
7186   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7187   {
7188     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7189       MovDir[x][y] = left_dir;
7190     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7191       MovDir[x][y] = right_dir;
7192
7193     if (MovDir[x][y] != old_move_dir)
7194       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7195   }
7196   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7197   {
7198     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7199       MovDir[x][y] = right_dir;
7200     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7201       MovDir[x][y] = left_dir;
7202
7203     if (MovDir[x][y] != old_move_dir)
7204       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7205   }
7206   else if (move_pattern == MV_TOWARDS_PLAYER ||
7207            move_pattern == MV_AWAY_FROM_PLAYER)
7208   {
7209     int attr_x = -1, attr_y = -1;
7210     int newx, newy;
7211     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7212
7213     if (AllPlayersGone)
7214     {
7215       attr_x = ExitX;
7216       attr_y = ExitY;
7217     }
7218     else
7219     {
7220       int i;
7221
7222       for (i = 0; i < MAX_PLAYERS; i++)
7223       {
7224         struct PlayerInfo *player = &stored_player[i];
7225         int jx = player->jx, jy = player->jy;
7226
7227         if (!player->active)
7228           continue;
7229
7230         if (attr_x == -1 ||
7231             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7232         {
7233           attr_x = jx;
7234           attr_y = jy;
7235         }
7236       }
7237     }
7238
7239     MovDir[x][y] = MV_NONE;
7240     if (attr_x < x)
7241       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7242     else if (attr_x > x)
7243       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7244     if (attr_y < y)
7245       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7246     else if (attr_y > y)
7247       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7248
7249     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7250
7251     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7252     {
7253       boolean first_horiz = RND(2);
7254       int new_move_dir = MovDir[x][y];
7255
7256       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7257       {
7258         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7259         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7260
7261         return;
7262       }
7263
7264       MovDir[x][y] =
7265         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7266       Moving2Blocked(x, y, &newx, &newy);
7267
7268       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7269         return;
7270
7271       MovDir[x][y] =
7272         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7273       Moving2Blocked(x, y, &newx, &newy);
7274
7275       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7276         return;
7277
7278       MovDir[x][y] = old_move_dir;
7279     }
7280   }
7281   else if (move_pattern == MV_WHEN_PUSHED ||
7282            move_pattern == MV_WHEN_DROPPED)
7283   {
7284     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7285       MovDir[x][y] = MV_NONE;
7286
7287     MovDelay[x][y] = 0;
7288   }
7289   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7290   {
7291     static int test_xy[7][2] =
7292     {
7293       { 0, -1 },
7294       { -1, 0 },
7295       { +1, 0 },
7296       { 0, +1 },
7297       { 0, -1 },
7298       { -1, 0 },
7299       { +1, 0 },
7300     };
7301     static int test_dir[7] =
7302     {
7303       MV_UP,
7304       MV_LEFT,
7305       MV_RIGHT,
7306       MV_DOWN,
7307       MV_UP,
7308       MV_LEFT,
7309       MV_RIGHT,
7310     };
7311     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7312     int move_preference = -1000000;     /* start with very low preference */
7313     int new_move_dir = MV_NONE;
7314     int start_test = RND(4);
7315     int i;
7316
7317     for (i = 0; i < NUM_DIRECTIONS; i++)
7318     {
7319       int move_dir = test_dir[start_test + i];
7320       int move_dir_preference;
7321
7322       xx = x + test_xy[start_test + i][0];
7323       yy = y + test_xy[start_test + i][1];
7324
7325       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7326           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7327       {
7328         new_move_dir = move_dir;
7329
7330         break;
7331       }
7332
7333       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7334         continue;
7335
7336       move_dir_preference = -1 * RunnerVisit[xx][yy];
7337       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7338         move_dir_preference = PlayerVisit[xx][yy];
7339
7340       if (move_dir_preference > move_preference)
7341       {
7342         /* prefer field that has not been visited for the longest time */
7343         move_preference = move_dir_preference;
7344         new_move_dir = move_dir;
7345       }
7346       else if (move_dir_preference == move_preference &&
7347                move_dir == old_move_dir)
7348       {
7349         /* prefer last direction when all directions are preferred equally */
7350         move_preference = move_dir_preference;
7351         new_move_dir = move_dir;
7352       }
7353     }
7354
7355     MovDir[x][y] = new_move_dir;
7356     if (old_move_dir != new_move_dir)
7357       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7358   }
7359 }
7360
7361 static void TurnRound(int x, int y)
7362 {
7363   int direction = MovDir[x][y];
7364
7365   TurnRoundExt(x, y);
7366
7367   GfxDir[x][y] = MovDir[x][y];
7368
7369   if (direction != MovDir[x][y])
7370     GfxFrame[x][y] = 0;
7371
7372   if (MovDelay[x][y])
7373     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7374
7375   ResetGfxFrame(x, y);
7376 }
7377
7378 static boolean JustBeingPushed(int x, int y)
7379 {
7380   int i;
7381
7382   for (i = 0; i < MAX_PLAYERS; i++)
7383   {
7384     struct PlayerInfo *player = &stored_player[i];
7385
7386     if (player->active && player->is_pushing && player->MovPos)
7387     {
7388       int next_jx = player->jx + (player->jx - player->last_jx);
7389       int next_jy = player->jy + (player->jy - player->last_jy);
7390
7391       if (x == next_jx && y == next_jy)
7392         return TRUE;
7393     }
7394   }
7395
7396   return FALSE;
7397 }
7398
7399 void StartMoving(int x, int y)
7400 {
7401   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7402   int element = Feld[x][y];
7403
7404   if (Stop[x][y])
7405     return;
7406
7407   if (MovDelay[x][y] == 0)
7408     GfxAction[x][y] = ACTION_DEFAULT;
7409
7410   if (CAN_FALL(element) && y < lev_fieldy - 1)
7411   {
7412     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7413         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7414       if (JustBeingPushed(x, y))
7415         return;
7416
7417     if (element == EL_QUICKSAND_FULL)
7418     {
7419       if (IS_FREE(x, y + 1))
7420       {
7421         InitMovingField(x, y, MV_DOWN);
7422         started_moving = TRUE;
7423
7424         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7425 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7426         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7427           Store[x][y] = EL_ROCK;
7428 #else
7429         Store[x][y] = EL_ROCK;
7430 #endif
7431
7432         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7433       }
7434       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7435       {
7436         if (!MovDelay[x][y])
7437         {
7438           MovDelay[x][y] = TILEY + 1;
7439
7440           ResetGfxAnimation(x, y);
7441           ResetGfxAnimation(x, y + 1);
7442         }
7443
7444         if (MovDelay[x][y])
7445         {
7446           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7447           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7448
7449           MovDelay[x][y]--;
7450           if (MovDelay[x][y])
7451             return;
7452         }
7453
7454         Feld[x][y] = EL_QUICKSAND_EMPTY;
7455         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7456         Store[x][y + 1] = Store[x][y];
7457         Store[x][y] = 0;
7458
7459         PlayLevelSoundAction(x, y, ACTION_FILLING);
7460       }
7461       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7462       {
7463         if (!MovDelay[x][y])
7464         {
7465           MovDelay[x][y] = TILEY + 1;
7466
7467           ResetGfxAnimation(x, y);
7468           ResetGfxAnimation(x, y + 1);
7469         }
7470
7471         if (MovDelay[x][y])
7472         {
7473           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7474           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7475
7476           MovDelay[x][y]--;
7477           if (MovDelay[x][y])
7478             return;
7479         }
7480
7481         Feld[x][y] = EL_QUICKSAND_EMPTY;
7482         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7483         Store[x][y + 1] = Store[x][y];
7484         Store[x][y] = 0;
7485
7486         PlayLevelSoundAction(x, y, ACTION_FILLING);
7487       }
7488     }
7489     else if (element == EL_QUICKSAND_FAST_FULL)
7490     {
7491       if (IS_FREE(x, y + 1))
7492       {
7493         InitMovingField(x, y, MV_DOWN);
7494         started_moving = TRUE;
7495
7496         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7497 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7498         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7499           Store[x][y] = EL_ROCK;
7500 #else
7501         Store[x][y] = EL_ROCK;
7502 #endif
7503
7504         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7505       }
7506       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7507       {
7508         if (!MovDelay[x][y])
7509         {
7510           MovDelay[x][y] = TILEY + 1;
7511
7512           ResetGfxAnimation(x, y);
7513           ResetGfxAnimation(x, y + 1);
7514         }
7515
7516         if (MovDelay[x][y])
7517         {
7518           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7519           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7520
7521           MovDelay[x][y]--;
7522           if (MovDelay[x][y])
7523             return;
7524         }
7525
7526         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7527         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7528         Store[x][y + 1] = Store[x][y];
7529         Store[x][y] = 0;
7530
7531         PlayLevelSoundAction(x, y, ACTION_FILLING);
7532       }
7533       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7534       {
7535         if (!MovDelay[x][y])
7536         {
7537           MovDelay[x][y] = TILEY + 1;
7538
7539           ResetGfxAnimation(x, y);
7540           ResetGfxAnimation(x, y + 1);
7541         }
7542
7543         if (MovDelay[x][y])
7544         {
7545           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7546           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7547
7548           MovDelay[x][y]--;
7549           if (MovDelay[x][y])
7550             return;
7551         }
7552
7553         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7554         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7555         Store[x][y + 1] = Store[x][y];
7556         Store[x][y] = 0;
7557
7558         PlayLevelSoundAction(x, y, ACTION_FILLING);
7559       }
7560     }
7561     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7562              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7563     {
7564       InitMovingField(x, y, MV_DOWN);
7565       started_moving = TRUE;
7566
7567       Feld[x][y] = EL_QUICKSAND_FILLING;
7568       Store[x][y] = element;
7569
7570       PlayLevelSoundAction(x, y, ACTION_FILLING);
7571     }
7572     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7573              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7574     {
7575       InitMovingField(x, y, MV_DOWN);
7576       started_moving = TRUE;
7577
7578       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7579       Store[x][y] = element;
7580
7581       PlayLevelSoundAction(x, y, ACTION_FILLING);
7582     }
7583     else if (element == EL_MAGIC_WALL_FULL)
7584     {
7585       if (IS_FREE(x, y + 1))
7586       {
7587         InitMovingField(x, y, MV_DOWN);
7588         started_moving = TRUE;
7589
7590         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7591         Store[x][y] = EL_CHANGED(Store[x][y]);
7592       }
7593       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7594       {
7595         if (!MovDelay[x][y])
7596           MovDelay[x][y] = TILEY / 4 + 1;
7597
7598         if (MovDelay[x][y])
7599         {
7600           MovDelay[x][y]--;
7601           if (MovDelay[x][y])
7602             return;
7603         }
7604
7605         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7606         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7607         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7608         Store[x][y] = 0;
7609       }
7610     }
7611     else if (element == EL_BD_MAGIC_WALL_FULL)
7612     {
7613       if (IS_FREE(x, y + 1))
7614       {
7615         InitMovingField(x, y, MV_DOWN);
7616         started_moving = TRUE;
7617
7618         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7619         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7620       }
7621       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7622       {
7623         if (!MovDelay[x][y])
7624           MovDelay[x][y] = TILEY / 4 + 1;
7625
7626         if (MovDelay[x][y])
7627         {
7628           MovDelay[x][y]--;
7629           if (MovDelay[x][y])
7630             return;
7631         }
7632
7633         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7634         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7635         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7636         Store[x][y] = 0;
7637       }
7638     }
7639     else if (element == EL_DC_MAGIC_WALL_FULL)
7640     {
7641       if (IS_FREE(x, y + 1))
7642       {
7643         InitMovingField(x, y, MV_DOWN);
7644         started_moving = TRUE;
7645
7646         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7647         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7648       }
7649       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7650       {
7651         if (!MovDelay[x][y])
7652           MovDelay[x][y] = TILEY / 4 + 1;
7653
7654         if (MovDelay[x][y])
7655         {
7656           MovDelay[x][y]--;
7657           if (MovDelay[x][y])
7658             return;
7659         }
7660
7661         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7662         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7663         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7664         Store[x][y] = 0;
7665       }
7666     }
7667     else if ((CAN_PASS_MAGIC_WALL(element) &&
7668               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7669                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7670              (CAN_PASS_DC_MAGIC_WALL(element) &&
7671               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7672
7673     {
7674       InitMovingField(x, y, MV_DOWN);
7675       started_moving = TRUE;
7676
7677       Feld[x][y] =
7678         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7679          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7680          EL_DC_MAGIC_WALL_FILLING);
7681       Store[x][y] = element;
7682     }
7683     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7684     {
7685       SplashAcid(x, y + 1);
7686
7687       InitMovingField(x, y, MV_DOWN);
7688       started_moving = TRUE;
7689
7690       Store[x][y] = EL_ACID;
7691     }
7692     else if (
7693              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7694               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7695              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7696               CAN_FALL(element) && WasJustFalling[x][y] &&
7697               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7698
7699              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7700               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7701               (Feld[x][y + 1] == EL_BLOCKED)))
7702     {
7703       /* this is needed for a special case not covered by calling "Impact()"
7704          from "ContinueMoving()": if an element moves to a tile directly below
7705          another element which was just falling on that tile (which was empty
7706          in the previous frame), the falling element above would just stop
7707          instead of smashing the element below (in previous version, the above
7708          element was just checked for "moving" instead of "falling", resulting
7709          in incorrect smashes caused by horizontal movement of the above
7710          element; also, the case of the player being the element to smash was
7711          simply not covered here... :-/ ) */
7712
7713       CheckCollision[x][y] = 0;
7714       CheckImpact[x][y] = 0;
7715
7716       Impact(x, y);
7717     }
7718     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7719     {
7720       if (MovDir[x][y] == MV_NONE)
7721       {
7722         InitMovingField(x, y, MV_DOWN);
7723         started_moving = TRUE;
7724       }
7725     }
7726     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7727     {
7728       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7729         MovDir[x][y] = MV_DOWN;
7730
7731       InitMovingField(x, y, MV_DOWN);
7732       started_moving = TRUE;
7733     }
7734     else if (element == EL_AMOEBA_DROP)
7735     {
7736       Feld[x][y] = EL_AMOEBA_GROWING;
7737       Store[x][y] = EL_AMOEBA_WET;
7738     }
7739     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7740               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7741              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7742              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7743     {
7744       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7745                                 (IS_FREE(x - 1, y + 1) ||
7746                                  Feld[x - 1][y + 1] == EL_ACID));
7747       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7748                                 (IS_FREE(x + 1, y + 1) ||
7749                                  Feld[x + 1][y + 1] == EL_ACID));
7750       boolean can_fall_any  = (can_fall_left || can_fall_right);
7751       boolean can_fall_both = (can_fall_left && can_fall_right);
7752       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7753
7754       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7755       {
7756         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7757           can_fall_right = FALSE;
7758         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7759           can_fall_left = FALSE;
7760         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7761           can_fall_right = FALSE;
7762         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7763           can_fall_left = FALSE;
7764
7765         can_fall_any  = (can_fall_left || can_fall_right);
7766         can_fall_both = FALSE;
7767       }
7768
7769       if (can_fall_both)
7770       {
7771         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7772           can_fall_right = FALSE;       /* slip down on left side */
7773         else
7774           can_fall_left = !(can_fall_right = RND(2));
7775
7776         can_fall_both = FALSE;
7777       }
7778
7779       if (can_fall_any)
7780       {
7781         /* if not determined otherwise, prefer left side for slipping down */
7782         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7783         started_moving = TRUE;
7784       }
7785     }
7786     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7787     {
7788       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7789       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7790       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7791       int belt_dir = game.belt_dir[belt_nr];
7792
7793       if ((belt_dir == MV_LEFT  && left_is_free) ||
7794           (belt_dir == MV_RIGHT && right_is_free))
7795       {
7796         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7797
7798         InitMovingField(x, y, belt_dir);
7799         started_moving = TRUE;
7800
7801         Pushed[x][y] = TRUE;
7802         Pushed[nextx][y] = TRUE;
7803
7804         GfxAction[x][y] = ACTION_DEFAULT;
7805       }
7806       else
7807       {
7808         MovDir[x][y] = 0;       /* if element was moving, stop it */
7809       }
7810     }
7811   }
7812
7813   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7814   if (CAN_MOVE(element) && !started_moving)
7815   {
7816     int move_pattern = element_info[element].move_pattern;
7817     int newx, newy;
7818
7819     Moving2Blocked(x, y, &newx, &newy);
7820
7821     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7822       return;
7823
7824     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7825         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7826     {
7827       WasJustMoving[x][y] = 0;
7828       CheckCollision[x][y] = 0;
7829
7830       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7831
7832       if (Feld[x][y] != element)        /* element has changed */
7833         return;
7834     }
7835
7836     if (!MovDelay[x][y])        /* start new movement phase */
7837     {
7838       /* all objects that can change their move direction after each step
7839          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7840
7841       if (element != EL_YAMYAM &&
7842           element != EL_DARK_YAMYAM &&
7843           element != EL_PACMAN &&
7844           !(move_pattern & MV_ANY_DIRECTION) &&
7845           move_pattern != MV_TURNING_LEFT &&
7846           move_pattern != MV_TURNING_RIGHT &&
7847           move_pattern != MV_TURNING_LEFT_RIGHT &&
7848           move_pattern != MV_TURNING_RIGHT_LEFT &&
7849           move_pattern != MV_TURNING_RANDOM)
7850       {
7851         TurnRound(x, y);
7852
7853         if (MovDelay[x][y] && (element == EL_BUG ||
7854                                element == EL_SPACESHIP ||
7855                                element == EL_SP_SNIKSNAK ||
7856                                element == EL_SP_ELECTRON ||
7857                                element == EL_MOLE))
7858           TEST_DrawLevelField(x, y);
7859       }
7860     }
7861
7862     if (MovDelay[x][y])         /* wait some time before next movement */
7863     {
7864       MovDelay[x][y]--;
7865
7866       if (element == EL_ROBOT ||
7867           element == EL_YAMYAM ||
7868           element == EL_DARK_YAMYAM)
7869       {
7870         DrawLevelElementAnimationIfNeeded(x, y, element);
7871         PlayLevelSoundAction(x, y, ACTION_WAITING);
7872       }
7873       else if (element == EL_SP_ELECTRON)
7874         DrawLevelElementAnimationIfNeeded(x, y, element);
7875       else if (element == EL_DRAGON)
7876       {
7877         int i;
7878         int dir = MovDir[x][y];
7879         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7880         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7881         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7882                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7883                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7884                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7885         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7886
7887         GfxAction[x][y] = ACTION_ATTACKING;
7888
7889         if (IS_PLAYER(x, y))
7890           DrawPlayerField(x, y);
7891         else
7892           TEST_DrawLevelField(x, y);
7893
7894         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7895
7896         for (i = 1; i <= 3; i++)
7897         {
7898           int xx = x + i * dx;
7899           int yy = y + i * dy;
7900           int sx = SCREENX(xx);
7901           int sy = SCREENY(yy);
7902           int flame_graphic = graphic + (i - 1);
7903
7904           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7905             break;
7906
7907           if (MovDelay[x][y])
7908           {
7909             int flamed = MovingOrBlocked2Element(xx, yy);
7910
7911             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7912               Bang(xx, yy);
7913             else
7914               RemoveMovingField(xx, yy);
7915
7916             ChangeDelay[xx][yy] = 0;
7917
7918             Feld[xx][yy] = EL_FLAMES;
7919
7920             if (IN_SCR_FIELD(sx, sy))
7921             {
7922               TEST_DrawLevelFieldCrumbled(xx, yy);
7923               DrawGraphic(sx, sy, flame_graphic, frame);
7924             }
7925           }
7926           else
7927           {
7928             if (Feld[xx][yy] == EL_FLAMES)
7929               Feld[xx][yy] = EL_EMPTY;
7930             TEST_DrawLevelField(xx, yy);
7931           }
7932         }
7933       }
7934
7935       if (MovDelay[x][y])       /* element still has to wait some time */
7936       {
7937         PlayLevelSoundAction(x, y, ACTION_WAITING);
7938
7939         return;
7940       }
7941     }
7942
7943     /* now make next step */
7944
7945     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7946
7947     if (DONT_COLLIDE_WITH(element) &&
7948         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7949         !PLAYER_ENEMY_PROTECTED(newx, newy))
7950     {
7951       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7952
7953       return;
7954     }
7955
7956     else if (CAN_MOVE_INTO_ACID(element) &&
7957              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7958              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7959              (MovDir[x][y] == MV_DOWN ||
7960               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7961     {
7962       SplashAcid(newx, newy);
7963       Store[x][y] = EL_ACID;
7964     }
7965     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7966     {
7967       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7968           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7969           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7970           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7971       {
7972         RemoveField(x, y);
7973         TEST_DrawLevelField(x, y);
7974
7975         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7976         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7977           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7978
7979         local_player->friends_still_needed--;
7980         if (!local_player->friends_still_needed &&
7981             !local_player->GameOver && AllPlayersGone)
7982           PlayerWins(local_player);
7983
7984         return;
7985       }
7986       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7987       {
7988         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7989           TEST_DrawLevelField(newx, newy);
7990         else
7991           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7992       }
7993       else if (!IS_FREE(newx, newy))
7994       {
7995         GfxAction[x][y] = ACTION_WAITING;
7996
7997         if (IS_PLAYER(x, y))
7998           DrawPlayerField(x, y);
7999         else
8000           TEST_DrawLevelField(x, y);
8001
8002         return;
8003       }
8004     }
8005     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8006     {
8007       if (IS_FOOD_PIG(Feld[newx][newy]))
8008       {
8009         if (IS_MOVING(newx, newy))
8010           RemoveMovingField(newx, newy);
8011         else
8012         {
8013           Feld[newx][newy] = EL_EMPTY;
8014           TEST_DrawLevelField(newx, newy);
8015         }
8016
8017         PlayLevelSound(x, y, SND_PIG_DIGGING);
8018       }
8019       else if (!IS_FREE(newx, newy))
8020       {
8021         if (IS_PLAYER(x, y))
8022           DrawPlayerField(x, y);
8023         else
8024           TEST_DrawLevelField(x, y);
8025
8026         return;
8027       }
8028     }
8029     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8030     {
8031       if (Store[x][y] != EL_EMPTY)
8032       {
8033         boolean can_clone = FALSE;
8034         int xx, yy;
8035
8036         /* check if element to clone is still there */
8037         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8038         {
8039           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8040           {
8041             can_clone = TRUE;
8042
8043             break;
8044           }
8045         }
8046
8047         /* cannot clone or target field not free anymore -- do not clone */
8048         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8049           Store[x][y] = EL_EMPTY;
8050       }
8051
8052       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8053       {
8054         if (IS_MV_DIAGONAL(MovDir[x][y]))
8055         {
8056           int diagonal_move_dir = MovDir[x][y];
8057           int stored = Store[x][y];
8058           int change_delay = 8;
8059           int graphic;
8060
8061           /* android is moving diagonally */
8062
8063           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8064
8065           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8066           GfxElement[x][y] = EL_EMC_ANDROID;
8067           GfxAction[x][y] = ACTION_SHRINKING;
8068           GfxDir[x][y] = diagonal_move_dir;
8069           ChangeDelay[x][y] = change_delay;
8070
8071           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8072                                    GfxDir[x][y]);
8073
8074           DrawLevelGraphicAnimation(x, y, graphic);
8075           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8076
8077           if (Feld[newx][newy] == EL_ACID)
8078           {
8079             SplashAcid(newx, newy);
8080
8081             return;
8082           }
8083
8084           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8085
8086           Store[newx][newy] = EL_EMC_ANDROID;
8087           GfxElement[newx][newy] = EL_EMC_ANDROID;
8088           GfxAction[newx][newy] = ACTION_GROWING;
8089           GfxDir[newx][newy] = diagonal_move_dir;
8090           ChangeDelay[newx][newy] = change_delay;
8091
8092           graphic = el_act_dir2img(GfxElement[newx][newy],
8093                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8094
8095           DrawLevelGraphicAnimation(newx, newy, graphic);
8096           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8097
8098           return;
8099         }
8100         else
8101         {
8102           Feld[newx][newy] = EL_EMPTY;
8103           TEST_DrawLevelField(newx, newy);
8104
8105           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8106         }
8107       }
8108       else if (!IS_FREE(newx, newy))
8109       {
8110         return;
8111       }
8112     }
8113     else if (IS_CUSTOM_ELEMENT(element) &&
8114              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8115     {
8116       if (!DigFieldByCE(newx, newy, element))
8117         return;
8118
8119       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8120       {
8121         RunnerVisit[x][y] = FrameCounter;
8122         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8123       }
8124     }
8125     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8126     {
8127       if (!IS_FREE(newx, newy))
8128       {
8129         if (IS_PLAYER(x, y))
8130           DrawPlayerField(x, y);
8131         else
8132           TEST_DrawLevelField(x, y);
8133
8134         return;
8135       }
8136       else
8137       {
8138         boolean wanna_flame = !RND(10);
8139         int dx = newx - x, dy = newy - y;
8140         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8141         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8142         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8143                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8144         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8145                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8146
8147         if ((wanna_flame ||
8148              IS_CLASSIC_ENEMY(element1) ||
8149              IS_CLASSIC_ENEMY(element2)) &&
8150             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8151             element1 != EL_FLAMES && element2 != EL_FLAMES)
8152         {
8153           ResetGfxAnimation(x, y);
8154           GfxAction[x][y] = ACTION_ATTACKING;
8155
8156           if (IS_PLAYER(x, y))
8157             DrawPlayerField(x, y);
8158           else
8159             TEST_DrawLevelField(x, y);
8160
8161           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8162
8163           MovDelay[x][y] = 50;
8164
8165           Feld[newx][newy] = EL_FLAMES;
8166           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8167             Feld[newx1][newy1] = EL_FLAMES;
8168           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8169             Feld[newx2][newy2] = EL_FLAMES;
8170
8171           return;
8172         }
8173       }
8174     }
8175     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8176              Feld[newx][newy] == EL_DIAMOND)
8177     {
8178       if (IS_MOVING(newx, newy))
8179         RemoveMovingField(newx, newy);
8180       else
8181       {
8182         Feld[newx][newy] = EL_EMPTY;
8183         TEST_DrawLevelField(newx, newy);
8184       }
8185
8186       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8187     }
8188     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8189              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8190     {
8191       if (AmoebaNr[newx][newy])
8192       {
8193         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8194         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8195             Feld[newx][newy] == EL_BD_AMOEBA)
8196           AmoebaCnt[AmoebaNr[newx][newy]]--;
8197       }
8198
8199       if (IS_MOVING(newx, newy))
8200       {
8201         RemoveMovingField(newx, newy);
8202       }
8203       else
8204       {
8205         Feld[newx][newy] = EL_EMPTY;
8206         TEST_DrawLevelField(newx, newy);
8207       }
8208
8209       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8210     }
8211     else if ((element == EL_PACMAN || element == EL_MOLE)
8212              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8213     {
8214       if (AmoebaNr[newx][newy])
8215       {
8216         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8217         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8218             Feld[newx][newy] == EL_BD_AMOEBA)
8219           AmoebaCnt[AmoebaNr[newx][newy]]--;
8220       }
8221
8222       if (element == EL_MOLE)
8223       {
8224         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8225         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8226
8227         ResetGfxAnimation(x, y);
8228         GfxAction[x][y] = ACTION_DIGGING;
8229         TEST_DrawLevelField(x, y);
8230
8231         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8232
8233         return;                         /* wait for shrinking amoeba */
8234       }
8235       else      /* element == EL_PACMAN */
8236       {
8237         Feld[newx][newy] = EL_EMPTY;
8238         TEST_DrawLevelField(newx, newy);
8239         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8240       }
8241     }
8242     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8243              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8244               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8245     {
8246       /* wait for shrinking amoeba to completely disappear */
8247       return;
8248     }
8249     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8250     {
8251       /* object was running against a wall */
8252
8253       TurnRound(x, y);
8254
8255       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8256         DrawLevelElementAnimation(x, y, element);
8257
8258       if (DONT_TOUCH(element))
8259         TestIfBadThingTouchesPlayer(x, y);
8260
8261       return;
8262     }
8263
8264     InitMovingField(x, y, MovDir[x][y]);
8265
8266     PlayLevelSoundAction(x, y, ACTION_MOVING);
8267   }
8268
8269   if (MovDir[x][y])
8270     ContinueMoving(x, y);
8271 }
8272
8273 void ContinueMoving(int x, int y)
8274 {
8275   int element = Feld[x][y];
8276   struct ElementInfo *ei = &element_info[element];
8277   int direction = MovDir[x][y];
8278   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8279   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8280   int newx = x + dx, newy = y + dy;
8281   int stored = Store[x][y];
8282   int stored_new = Store[newx][newy];
8283   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8284   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8285   boolean last_line = (newy == lev_fieldy - 1);
8286
8287   MovPos[x][y] += getElementMoveStepsize(x, y);
8288
8289   if (pushed_by_player) /* special case: moving object pushed by player */
8290     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8291
8292   if (ABS(MovPos[x][y]) < TILEX)
8293   {
8294     TEST_DrawLevelField(x, y);
8295
8296     return;     /* element is still moving */
8297   }
8298
8299   /* element reached destination field */
8300
8301   Feld[x][y] = EL_EMPTY;
8302   Feld[newx][newy] = element;
8303   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8304
8305   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8306   {
8307     element = Feld[newx][newy] = EL_ACID;
8308   }
8309   else if (element == EL_MOLE)
8310   {
8311     Feld[x][y] = EL_SAND;
8312
8313     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8314   }
8315   else if (element == EL_QUICKSAND_FILLING)
8316   {
8317     element = Feld[newx][newy] = get_next_element(element);
8318     Store[newx][newy] = Store[x][y];
8319   }
8320   else if (element == EL_QUICKSAND_EMPTYING)
8321   {
8322     Feld[x][y] = get_next_element(element);
8323     element = Feld[newx][newy] = Store[x][y];
8324   }
8325   else if (element == EL_QUICKSAND_FAST_FILLING)
8326   {
8327     element = Feld[newx][newy] = get_next_element(element);
8328     Store[newx][newy] = Store[x][y];
8329   }
8330   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8331   {
8332     Feld[x][y] = get_next_element(element);
8333     element = Feld[newx][newy] = Store[x][y];
8334   }
8335   else if (element == EL_MAGIC_WALL_FILLING)
8336   {
8337     element = Feld[newx][newy] = get_next_element(element);
8338     if (!game.magic_wall_active)
8339       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8340     Store[newx][newy] = Store[x][y];
8341   }
8342   else if (element == EL_MAGIC_WALL_EMPTYING)
8343   {
8344     Feld[x][y] = get_next_element(element);
8345     if (!game.magic_wall_active)
8346       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8347     element = Feld[newx][newy] = Store[x][y];
8348
8349     InitField(newx, newy, FALSE);
8350   }
8351   else if (element == EL_BD_MAGIC_WALL_FILLING)
8352   {
8353     element = Feld[newx][newy] = get_next_element(element);
8354     if (!game.magic_wall_active)
8355       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8356     Store[newx][newy] = Store[x][y];
8357   }
8358   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8359   {
8360     Feld[x][y] = get_next_element(element);
8361     if (!game.magic_wall_active)
8362       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8363     element = Feld[newx][newy] = Store[x][y];
8364
8365     InitField(newx, newy, FALSE);
8366   }
8367   else if (element == EL_DC_MAGIC_WALL_FILLING)
8368   {
8369     element = Feld[newx][newy] = get_next_element(element);
8370     if (!game.magic_wall_active)
8371       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8372     Store[newx][newy] = Store[x][y];
8373   }
8374   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8375   {
8376     Feld[x][y] = get_next_element(element);
8377     if (!game.magic_wall_active)
8378       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8379     element = Feld[newx][newy] = Store[x][y];
8380
8381     InitField(newx, newy, FALSE);
8382   }
8383   else if (element == EL_AMOEBA_DROPPING)
8384   {
8385     Feld[x][y] = get_next_element(element);
8386     element = Feld[newx][newy] = Store[x][y];
8387   }
8388   else if (element == EL_SOKOBAN_OBJECT)
8389   {
8390     if (Back[x][y])
8391       Feld[x][y] = Back[x][y];
8392
8393     if (Back[newx][newy])
8394       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8395
8396     Back[x][y] = Back[newx][newy] = 0;
8397   }
8398
8399   Store[x][y] = EL_EMPTY;
8400   MovPos[x][y] = 0;
8401   MovDir[x][y] = 0;
8402   MovDelay[x][y] = 0;
8403
8404   MovDelay[newx][newy] = 0;
8405
8406   if (CAN_CHANGE_OR_HAS_ACTION(element))
8407   {
8408     /* copy element change control values to new field */
8409     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8410     ChangePage[newx][newy]  = ChangePage[x][y];
8411     ChangeCount[newx][newy] = ChangeCount[x][y];
8412     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8413   }
8414
8415   CustomValue[newx][newy] = CustomValue[x][y];
8416
8417   ChangeDelay[x][y] = 0;
8418   ChangePage[x][y] = -1;
8419   ChangeCount[x][y] = 0;
8420   ChangeEvent[x][y] = -1;
8421
8422   CustomValue[x][y] = 0;
8423
8424   /* copy animation control values to new field */
8425   GfxFrame[newx][newy]  = GfxFrame[x][y];
8426   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8427   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8428   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8429
8430   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8431
8432   /* some elements can leave other elements behind after moving */
8433   if (ei->move_leave_element != EL_EMPTY &&
8434       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8435       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8436   {
8437     int move_leave_element = ei->move_leave_element;
8438
8439     /* this makes it possible to leave the removed element again */
8440     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8441       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8442
8443     Feld[x][y] = move_leave_element;
8444
8445     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8446       MovDir[x][y] = direction;
8447
8448     InitField(x, y, FALSE);
8449
8450     if (GFX_CRUMBLED(Feld[x][y]))
8451       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8452
8453     if (ELEM_IS_PLAYER(move_leave_element))
8454       RelocatePlayer(x, y, move_leave_element);
8455   }
8456
8457   /* do this after checking for left-behind element */
8458   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8459
8460   if (!CAN_MOVE(element) ||
8461       (CAN_FALL(element) && direction == MV_DOWN &&
8462        (element == EL_SPRING ||
8463         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8464         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8465     GfxDir[x][y] = MovDir[newx][newy] = 0;
8466
8467   TEST_DrawLevelField(x, y);
8468   TEST_DrawLevelField(newx, newy);
8469
8470   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8471
8472   /* prevent pushed element from moving on in pushed direction */
8473   if (pushed_by_player && CAN_MOVE(element) &&
8474       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8475       !(element_info[element].move_pattern & direction))
8476     TurnRound(newx, newy);
8477
8478   /* prevent elements on conveyor belt from moving on in last direction */
8479   if (pushed_by_conveyor && CAN_FALL(element) &&
8480       direction & MV_HORIZONTAL)
8481     MovDir[newx][newy] = 0;
8482
8483   if (!pushed_by_player)
8484   {
8485     int nextx = newx + dx, nexty = newy + dy;
8486     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8487
8488     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8489
8490     if (CAN_FALL(element) && direction == MV_DOWN)
8491       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8492
8493     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8494       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8495
8496     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8497       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8498   }
8499
8500   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8501   {
8502     TestIfBadThingTouchesPlayer(newx, newy);
8503     TestIfBadThingTouchesFriend(newx, newy);
8504
8505     if (!IS_CUSTOM_ELEMENT(element))
8506       TestIfBadThingTouchesOtherBadThing(newx, newy);
8507   }
8508   else if (element == EL_PENGUIN)
8509     TestIfFriendTouchesBadThing(newx, newy);
8510
8511   if (DONT_GET_HIT_BY(element))
8512   {
8513     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8514   }
8515
8516   /* give the player one last chance (one more frame) to move away */
8517   if (CAN_FALL(element) && direction == MV_DOWN &&
8518       (last_line || (!IS_FREE(x, newy + 1) &&
8519                      (!IS_PLAYER(x, newy + 1) ||
8520                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8521     Impact(x, newy);
8522
8523   if (pushed_by_player && !game.use_change_when_pushing_bug)
8524   {
8525     int push_side = MV_DIR_OPPOSITE(direction);
8526     struct PlayerInfo *player = PLAYERINFO(x, y);
8527
8528     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8529                                player->index_bit, push_side);
8530     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8531                                         player->index_bit, push_side);
8532   }
8533
8534   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8535     MovDelay[newx][newy] = 1;
8536
8537   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8538
8539   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8540   TestIfElementHitsCustomElement(newx, newy, direction);
8541   TestIfPlayerTouchesCustomElement(newx, newy);
8542   TestIfElementTouchesCustomElement(newx, newy);
8543
8544   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8545       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8546     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8547                              MV_DIR_OPPOSITE(direction));
8548 }
8549
8550 int AmoebeNachbarNr(int ax, int ay)
8551 {
8552   int i;
8553   int element = Feld[ax][ay];
8554   int group_nr = 0;
8555   static int xy[4][2] =
8556   {
8557     { 0, -1 },
8558     { -1, 0 },
8559     { +1, 0 },
8560     { 0, +1 }
8561   };
8562
8563   for (i = 0; i < NUM_DIRECTIONS; i++)
8564   {
8565     int x = ax + xy[i][0];
8566     int y = ay + xy[i][1];
8567
8568     if (!IN_LEV_FIELD(x, y))
8569       continue;
8570
8571     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8572       group_nr = AmoebaNr[x][y];
8573   }
8574
8575   return group_nr;
8576 }
8577
8578 void AmoebenVereinigen(int ax, int ay)
8579 {
8580   int i, x, y, xx, yy;
8581   int new_group_nr = AmoebaNr[ax][ay];
8582   static int xy[4][2] =
8583   {
8584     { 0, -1 },
8585     { -1, 0 },
8586     { +1, 0 },
8587     { 0, +1 }
8588   };
8589
8590   if (new_group_nr == 0)
8591     return;
8592
8593   for (i = 0; i < NUM_DIRECTIONS; i++)
8594   {
8595     x = ax + xy[i][0];
8596     y = ay + xy[i][1];
8597
8598     if (!IN_LEV_FIELD(x, y))
8599       continue;
8600
8601     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8602          Feld[x][y] == EL_BD_AMOEBA ||
8603          Feld[x][y] == EL_AMOEBA_DEAD) &&
8604         AmoebaNr[x][y] != new_group_nr)
8605     {
8606       int old_group_nr = AmoebaNr[x][y];
8607
8608       if (old_group_nr == 0)
8609         return;
8610
8611       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8612       AmoebaCnt[old_group_nr] = 0;
8613       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8614       AmoebaCnt2[old_group_nr] = 0;
8615
8616       SCAN_PLAYFIELD(xx, yy)
8617       {
8618         if (AmoebaNr[xx][yy] == old_group_nr)
8619           AmoebaNr[xx][yy] = new_group_nr;
8620       }
8621     }
8622   }
8623 }
8624
8625 void AmoebeUmwandeln(int ax, int ay)
8626 {
8627   int i, x, y;
8628
8629   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8630   {
8631     int group_nr = AmoebaNr[ax][ay];
8632
8633 #ifdef DEBUG
8634     if (group_nr == 0)
8635     {
8636       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8637       printf("AmoebeUmwandeln(): This should never happen!\n");
8638       return;
8639     }
8640 #endif
8641
8642     SCAN_PLAYFIELD(x, y)
8643     {
8644       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8645       {
8646         AmoebaNr[x][y] = 0;
8647         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8648       }
8649     }
8650
8651     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8652                             SND_AMOEBA_TURNING_TO_GEM :
8653                             SND_AMOEBA_TURNING_TO_ROCK));
8654     Bang(ax, ay);
8655   }
8656   else
8657   {
8658     static int xy[4][2] =
8659     {
8660       { 0, -1 },
8661       { -1, 0 },
8662       { +1, 0 },
8663       { 0, +1 }
8664     };
8665
8666     for (i = 0; i < NUM_DIRECTIONS; i++)
8667     {
8668       x = ax + xy[i][0];
8669       y = ay + xy[i][1];
8670
8671       if (!IN_LEV_FIELD(x, y))
8672         continue;
8673
8674       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8675       {
8676         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8677                               SND_AMOEBA_TURNING_TO_GEM :
8678                               SND_AMOEBA_TURNING_TO_ROCK));
8679         Bang(x, y);
8680       }
8681     }
8682   }
8683 }
8684
8685 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8686 {
8687   int x, y;
8688   int group_nr = AmoebaNr[ax][ay];
8689   boolean done = FALSE;
8690
8691 #ifdef DEBUG
8692   if (group_nr == 0)
8693   {
8694     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8695     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8696     return;
8697   }
8698 #endif
8699
8700   SCAN_PLAYFIELD(x, y)
8701   {
8702     if (AmoebaNr[x][y] == group_nr &&
8703         (Feld[x][y] == EL_AMOEBA_DEAD ||
8704          Feld[x][y] == EL_BD_AMOEBA ||
8705          Feld[x][y] == EL_AMOEBA_GROWING))
8706     {
8707       AmoebaNr[x][y] = 0;
8708       Feld[x][y] = new_element;
8709       InitField(x, y, FALSE);
8710       TEST_DrawLevelField(x, y);
8711       done = TRUE;
8712     }
8713   }
8714
8715   if (done)
8716     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8717                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8718                             SND_BD_AMOEBA_TURNING_TO_GEM));
8719 }
8720
8721 void AmoebeWaechst(int x, int y)
8722 {
8723   static unsigned int sound_delay = 0;
8724   static unsigned int sound_delay_value = 0;
8725
8726   if (!MovDelay[x][y])          /* start new growing cycle */
8727   {
8728     MovDelay[x][y] = 7;
8729
8730     if (DelayReached(&sound_delay, sound_delay_value))
8731     {
8732       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8733       sound_delay_value = 30;
8734     }
8735   }
8736
8737   if (MovDelay[x][y])           /* wait some time before growing bigger */
8738   {
8739     MovDelay[x][y]--;
8740     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8741     {
8742       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8743                                            6 - MovDelay[x][y]);
8744
8745       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8746     }
8747
8748     if (!MovDelay[x][y])
8749     {
8750       Feld[x][y] = Store[x][y];
8751       Store[x][y] = 0;
8752       TEST_DrawLevelField(x, y);
8753     }
8754   }
8755 }
8756
8757 void AmoebaDisappearing(int x, int y)
8758 {
8759   static unsigned int sound_delay = 0;
8760   static unsigned int sound_delay_value = 0;
8761
8762   if (!MovDelay[x][y])          /* start new shrinking cycle */
8763   {
8764     MovDelay[x][y] = 7;
8765
8766     if (DelayReached(&sound_delay, sound_delay_value))
8767       sound_delay_value = 30;
8768   }
8769
8770   if (MovDelay[x][y])           /* wait some time before shrinking */
8771   {
8772     MovDelay[x][y]--;
8773     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8774     {
8775       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8776                                            6 - MovDelay[x][y]);
8777
8778       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8779     }
8780
8781     if (!MovDelay[x][y])
8782     {
8783       Feld[x][y] = EL_EMPTY;
8784       TEST_DrawLevelField(x, y);
8785
8786       /* don't let mole enter this field in this cycle;
8787          (give priority to objects falling to this field from above) */
8788       Stop[x][y] = TRUE;
8789     }
8790   }
8791 }
8792
8793 void AmoebeAbleger(int ax, int ay)
8794 {
8795   int i;
8796   int element = Feld[ax][ay];
8797   int graphic = el2img(element);
8798   int newax = ax, neway = ay;
8799   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8800   static int xy[4][2] =
8801   {
8802     { 0, -1 },
8803     { -1, 0 },
8804     { +1, 0 },
8805     { 0, +1 }
8806   };
8807
8808   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8809   {
8810     Feld[ax][ay] = EL_AMOEBA_DEAD;
8811     TEST_DrawLevelField(ax, ay);
8812     return;
8813   }
8814
8815   if (IS_ANIMATED(graphic))
8816     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8817
8818   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8819     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8820
8821   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8822   {
8823     MovDelay[ax][ay]--;
8824     if (MovDelay[ax][ay])
8825       return;
8826   }
8827
8828   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8829   {
8830     int start = RND(4);
8831     int x = ax + xy[start][0];
8832     int y = ay + xy[start][1];
8833
8834     if (!IN_LEV_FIELD(x, y))
8835       return;
8836
8837     if (IS_FREE(x, y) ||
8838         CAN_GROW_INTO(Feld[x][y]) ||
8839         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8840         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8841     {
8842       newax = x;
8843       neway = y;
8844     }
8845
8846     if (newax == ax && neway == ay)
8847       return;
8848   }
8849   else                          /* normal or "filled" (BD style) amoeba */
8850   {
8851     int start = RND(4);
8852     boolean waiting_for_player = FALSE;
8853
8854     for (i = 0; i < NUM_DIRECTIONS; i++)
8855     {
8856       int j = (start + i) % 4;
8857       int x = ax + xy[j][0];
8858       int y = ay + xy[j][1];
8859
8860       if (!IN_LEV_FIELD(x, y))
8861         continue;
8862
8863       if (IS_FREE(x, y) ||
8864           CAN_GROW_INTO(Feld[x][y]) ||
8865           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8866           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8867       {
8868         newax = x;
8869         neway = y;
8870         break;
8871       }
8872       else if (IS_PLAYER(x, y))
8873         waiting_for_player = TRUE;
8874     }
8875
8876     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8877     {
8878       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8879       {
8880         Feld[ax][ay] = EL_AMOEBA_DEAD;
8881         TEST_DrawLevelField(ax, ay);
8882         AmoebaCnt[AmoebaNr[ax][ay]]--;
8883
8884         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8885         {
8886           if (element == EL_AMOEBA_FULL)
8887             AmoebeUmwandeln(ax, ay);
8888           else if (element == EL_BD_AMOEBA)
8889             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8890         }
8891       }
8892       return;
8893     }
8894     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8895     {
8896       /* amoeba gets larger by growing in some direction */
8897
8898       int new_group_nr = AmoebaNr[ax][ay];
8899
8900 #ifdef DEBUG
8901   if (new_group_nr == 0)
8902   {
8903     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8904     printf("AmoebeAbleger(): This should never happen!\n");
8905     return;
8906   }
8907 #endif
8908
8909       AmoebaNr[newax][neway] = new_group_nr;
8910       AmoebaCnt[new_group_nr]++;
8911       AmoebaCnt2[new_group_nr]++;
8912
8913       /* if amoeba touches other amoeba(s) after growing, unify them */
8914       AmoebenVereinigen(newax, neway);
8915
8916       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8917       {
8918         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8919         return;
8920       }
8921     }
8922   }
8923
8924   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8925       (neway == lev_fieldy - 1 && newax != ax))
8926   {
8927     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8928     Store[newax][neway] = element;
8929   }
8930   else if (neway == ay || element == EL_EMC_DRIPPER)
8931   {
8932     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8933
8934     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8935   }
8936   else
8937   {
8938     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8939     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8940     Store[ax][ay] = EL_AMOEBA_DROP;
8941     ContinueMoving(ax, ay);
8942     return;
8943   }
8944
8945   TEST_DrawLevelField(newax, neway);
8946 }
8947
8948 void Life(int ax, int ay)
8949 {
8950   int x1, y1, x2, y2;
8951   int life_time = 40;
8952   int element = Feld[ax][ay];
8953   int graphic = el2img(element);
8954   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8955                          level.biomaze);
8956   boolean changed = FALSE;
8957
8958   if (IS_ANIMATED(graphic))
8959     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8960
8961   if (Stop[ax][ay])
8962     return;
8963
8964   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8965     MovDelay[ax][ay] = life_time;
8966
8967   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8968   {
8969     MovDelay[ax][ay]--;
8970     if (MovDelay[ax][ay])
8971       return;
8972   }
8973
8974   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8975   {
8976     int xx = ax+x1, yy = ay+y1;
8977     int nachbarn = 0;
8978
8979     if (!IN_LEV_FIELD(xx, yy))
8980       continue;
8981
8982     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8983     {
8984       int x = xx+x2, y = yy+y2;
8985
8986       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8987         continue;
8988
8989       if (((Feld[x][y] == element ||
8990             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8991            !Stop[x][y]) ||
8992           (IS_FREE(x, y) && Stop[x][y]))
8993         nachbarn++;
8994     }
8995
8996     if (xx == ax && yy == ay)           /* field in the middle */
8997     {
8998       if (nachbarn < life_parameter[0] ||
8999           nachbarn > life_parameter[1])
9000       {
9001         Feld[xx][yy] = EL_EMPTY;
9002         if (!Stop[xx][yy])
9003           TEST_DrawLevelField(xx, yy);
9004         Stop[xx][yy] = TRUE;
9005         changed = TRUE;
9006       }
9007     }
9008     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9009     {                                   /* free border field */
9010       if (nachbarn >= life_parameter[2] &&
9011           nachbarn <= life_parameter[3])
9012       {
9013         Feld[xx][yy] = element;
9014         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9015         if (!Stop[xx][yy])
9016           TEST_DrawLevelField(xx, yy);
9017         Stop[xx][yy] = TRUE;
9018         changed = TRUE;
9019       }
9020     }
9021   }
9022
9023   if (changed)
9024     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9025                    SND_GAME_OF_LIFE_GROWING);
9026 }
9027
9028 static void InitRobotWheel(int x, int y)
9029 {
9030   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9031 }
9032
9033 static void RunRobotWheel(int x, int y)
9034 {
9035   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9036 }
9037
9038 static void StopRobotWheel(int x, int y)
9039 {
9040   if (ZX == x && ZY == y)
9041   {
9042     ZX = ZY = -1;
9043
9044     game.robot_wheel_active = FALSE;
9045   }
9046 }
9047
9048 static void InitTimegateWheel(int x, int y)
9049 {
9050   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9051 }
9052
9053 static void RunTimegateWheel(int x, int y)
9054 {
9055   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9056 }
9057
9058 static void InitMagicBallDelay(int x, int y)
9059 {
9060   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9061 }
9062
9063 static void ActivateMagicBall(int bx, int by)
9064 {
9065   int x, y;
9066
9067   if (level.ball_random)
9068   {
9069     int pos_border = RND(8);    /* select one of the eight border elements */
9070     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9071     int xx = pos_content % 3;
9072     int yy = pos_content / 3;
9073
9074     x = bx - 1 + xx;
9075     y = by - 1 + yy;
9076
9077     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9078       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9079   }
9080   else
9081   {
9082     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9083     {
9084       int xx = x - bx + 1;
9085       int yy = y - by + 1;
9086
9087       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9088         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9089     }
9090   }
9091
9092   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9093 }
9094
9095 void CheckExit(int x, int y)
9096 {
9097   if (local_player->gems_still_needed > 0 ||
9098       local_player->sokobanfields_still_needed > 0 ||
9099       local_player->lights_still_needed > 0)
9100   {
9101     int element = Feld[x][y];
9102     int graphic = el2img(element);
9103
9104     if (IS_ANIMATED(graphic))
9105       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9106
9107     return;
9108   }
9109
9110   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9111     return;
9112
9113   Feld[x][y] = EL_EXIT_OPENING;
9114
9115   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9116 }
9117
9118 void CheckExitEM(int x, int y)
9119 {
9120   if (local_player->gems_still_needed > 0 ||
9121       local_player->sokobanfields_still_needed > 0 ||
9122       local_player->lights_still_needed > 0)
9123   {
9124     int element = Feld[x][y];
9125     int graphic = el2img(element);
9126
9127     if (IS_ANIMATED(graphic))
9128       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9129
9130     return;
9131   }
9132
9133   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9134     return;
9135
9136   Feld[x][y] = EL_EM_EXIT_OPENING;
9137
9138   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9139 }
9140
9141 void CheckExitSteel(int x, int y)
9142 {
9143   if (local_player->gems_still_needed > 0 ||
9144       local_player->sokobanfields_still_needed > 0 ||
9145       local_player->lights_still_needed > 0)
9146   {
9147     int element = Feld[x][y];
9148     int graphic = el2img(element);
9149
9150     if (IS_ANIMATED(graphic))
9151       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9152
9153     return;
9154   }
9155
9156   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9157     return;
9158
9159   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9160
9161   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9162 }
9163
9164 void CheckExitSteelEM(int x, int y)
9165 {
9166   if (local_player->gems_still_needed > 0 ||
9167       local_player->sokobanfields_still_needed > 0 ||
9168       local_player->lights_still_needed > 0)
9169   {
9170     int element = Feld[x][y];
9171     int graphic = el2img(element);
9172
9173     if (IS_ANIMATED(graphic))
9174       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9175
9176     return;
9177   }
9178
9179   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9180     return;
9181
9182   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9183
9184   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9185 }
9186
9187 void CheckExitSP(int x, int y)
9188 {
9189   if (local_player->gems_still_needed > 0)
9190   {
9191     int element = Feld[x][y];
9192     int graphic = el2img(element);
9193
9194     if (IS_ANIMATED(graphic))
9195       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9196
9197     return;
9198   }
9199
9200   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9201     return;
9202
9203   Feld[x][y] = EL_SP_EXIT_OPENING;
9204
9205   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9206 }
9207
9208 static void CloseAllOpenTimegates()
9209 {
9210   int x, y;
9211
9212   SCAN_PLAYFIELD(x, y)
9213   {
9214     int element = Feld[x][y];
9215
9216     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9217     {
9218       Feld[x][y] = EL_TIMEGATE_CLOSING;
9219
9220       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9221     }
9222   }
9223 }
9224
9225 void DrawTwinkleOnField(int x, int y)
9226 {
9227   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9228     return;
9229
9230   if (Feld[x][y] == EL_BD_DIAMOND)
9231     return;
9232
9233   if (MovDelay[x][y] == 0)      /* next animation frame */
9234     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9235
9236   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9237   {
9238     MovDelay[x][y]--;
9239
9240     DrawLevelElementAnimation(x, y, Feld[x][y]);
9241
9242     if (MovDelay[x][y] != 0)
9243     {
9244       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9245                                            10 - MovDelay[x][y]);
9246
9247       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9248     }
9249   }
9250 }
9251
9252 void MauerWaechst(int x, int y)
9253 {
9254   int delay = 6;
9255
9256   if (!MovDelay[x][y])          /* next animation frame */
9257     MovDelay[x][y] = 3 * delay;
9258
9259   if (MovDelay[x][y])           /* wait some time before next frame */
9260   {
9261     MovDelay[x][y]--;
9262
9263     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9264     {
9265       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9266       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9267
9268       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9269     }
9270
9271     if (!MovDelay[x][y])
9272     {
9273       if (MovDir[x][y] == MV_LEFT)
9274       {
9275         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9276           TEST_DrawLevelField(x - 1, y);
9277       }
9278       else if (MovDir[x][y] == MV_RIGHT)
9279       {
9280         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9281           TEST_DrawLevelField(x + 1, y);
9282       }
9283       else if (MovDir[x][y] == MV_UP)
9284       {
9285         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9286           TEST_DrawLevelField(x, y - 1);
9287       }
9288       else
9289       {
9290         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9291           TEST_DrawLevelField(x, y + 1);
9292       }
9293
9294       Feld[x][y] = Store[x][y];
9295       Store[x][y] = 0;
9296       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9297       TEST_DrawLevelField(x, y);
9298     }
9299   }
9300 }
9301
9302 void MauerAbleger(int ax, int ay)
9303 {
9304   int element = Feld[ax][ay];
9305   int graphic = el2img(element);
9306   boolean oben_frei = FALSE, unten_frei = FALSE;
9307   boolean links_frei = FALSE, rechts_frei = FALSE;
9308   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9309   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9310   boolean new_wall = FALSE;
9311
9312   if (IS_ANIMATED(graphic))
9313     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9314
9315   if (!MovDelay[ax][ay])        /* start building new wall */
9316     MovDelay[ax][ay] = 6;
9317
9318   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9319   {
9320     MovDelay[ax][ay]--;
9321     if (MovDelay[ax][ay])
9322       return;
9323   }
9324
9325   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9326     oben_frei = TRUE;
9327   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9328     unten_frei = TRUE;
9329   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9330     links_frei = TRUE;
9331   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9332     rechts_frei = TRUE;
9333
9334   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9335       element == EL_EXPANDABLE_WALL_ANY)
9336   {
9337     if (oben_frei)
9338     {
9339       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9340       Store[ax][ay-1] = element;
9341       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9342       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9343         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9344                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9345       new_wall = TRUE;
9346     }
9347     if (unten_frei)
9348     {
9349       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9350       Store[ax][ay+1] = element;
9351       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9352       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9353         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9354                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9355       new_wall = TRUE;
9356     }
9357   }
9358
9359   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9360       element == EL_EXPANDABLE_WALL_ANY ||
9361       element == EL_EXPANDABLE_WALL ||
9362       element == EL_BD_EXPANDABLE_WALL)
9363   {
9364     if (links_frei)
9365     {
9366       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9367       Store[ax-1][ay] = element;
9368       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9369       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9370         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9371                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9372       new_wall = TRUE;
9373     }
9374
9375     if (rechts_frei)
9376     {
9377       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9378       Store[ax+1][ay] = element;
9379       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9380       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9381         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9382                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9383       new_wall = TRUE;
9384     }
9385   }
9386
9387   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9388     TEST_DrawLevelField(ax, ay);
9389
9390   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9391     oben_massiv = TRUE;
9392   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9393     unten_massiv = TRUE;
9394   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9395     links_massiv = TRUE;
9396   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9397     rechts_massiv = TRUE;
9398
9399   if (((oben_massiv && unten_massiv) ||
9400        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9401        element == EL_EXPANDABLE_WALL) &&
9402       ((links_massiv && rechts_massiv) ||
9403        element == EL_EXPANDABLE_WALL_VERTICAL))
9404     Feld[ax][ay] = EL_WALL;
9405
9406   if (new_wall)
9407     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9408 }
9409
9410 void MauerAblegerStahl(int ax, int ay)
9411 {
9412   int element = Feld[ax][ay];
9413   int graphic = el2img(element);
9414   boolean oben_frei = FALSE, unten_frei = FALSE;
9415   boolean links_frei = FALSE, rechts_frei = FALSE;
9416   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9417   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9418   boolean new_wall = FALSE;
9419
9420   if (IS_ANIMATED(graphic))
9421     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9422
9423   if (!MovDelay[ax][ay])        /* start building new wall */
9424     MovDelay[ax][ay] = 6;
9425
9426   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9427   {
9428     MovDelay[ax][ay]--;
9429     if (MovDelay[ax][ay])
9430       return;
9431   }
9432
9433   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9434     oben_frei = TRUE;
9435   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9436     unten_frei = TRUE;
9437   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9438     links_frei = TRUE;
9439   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9440     rechts_frei = TRUE;
9441
9442   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9443       element == EL_EXPANDABLE_STEELWALL_ANY)
9444   {
9445     if (oben_frei)
9446     {
9447       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9448       Store[ax][ay-1] = element;
9449       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9450       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9451         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9452                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9453       new_wall = TRUE;
9454     }
9455     if (unten_frei)
9456     {
9457       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9458       Store[ax][ay+1] = element;
9459       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9460       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9461         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9462                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9463       new_wall = TRUE;
9464     }
9465   }
9466
9467   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9468       element == EL_EXPANDABLE_STEELWALL_ANY)
9469   {
9470     if (links_frei)
9471     {
9472       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9473       Store[ax-1][ay] = element;
9474       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9475       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9476         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9477                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9478       new_wall = TRUE;
9479     }
9480
9481     if (rechts_frei)
9482     {
9483       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9484       Store[ax+1][ay] = element;
9485       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9486       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9487         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9488                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9489       new_wall = TRUE;
9490     }
9491   }
9492
9493   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9494     oben_massiv = TRUE;
9495   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9496     unten_massiv = TRUE;
9497   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9498     links_massiv = TRUE;
9499   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9500     rechts_massiv = TRUE;
9501
9502   if (((oben_massiv && unten_massiv) ||
9503        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9504       ((links_massiv && rechts_massiv) ||
9505        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9506     Feld[ax][ay] = EL_STEELWALL;
9507
9508   if (new_wall)
9509     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9510 }
9511
9512 void CheckForDragon(int x, int y)
9513 {
9514   int i, j;
9515   boolean dragon_found = FALSE;
9516   static int xy[4][2] =
9517   {
9518     { 0, -1 },
9519     { -1, 0 },
9520     { +1, 0 },
9521     { 0, +1 }
9522   };
9523
9524   for (i = 0; i < NUM_DIRECTIONS; i++)
9525   {
9526     for (j = 0; j < 4; j++)
9527     {
9528       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9529
9530       if (IN_LEV_FIELD(xx, yy) &&
9531           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9532       {
9533         if (Feld[xx][yy] == EL_DRAGON)
9534           dragon_found = TRUE;
9535       }
9536       else
9537         break;
9538     }
9539   }
9540
9541   if (!dragon_found)
9542   {
9543     for (i = 0; i < NUM_DIRECTIONS; i++)
9544     {
9545       for (j = 0; j < 3; j++)
9546       {
9547         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9548   
9549         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9550         {
9551           Feld[xx][yy] = EL_EMPTY;
9552           TEST_DrawLevelField(xx, yy);
9553         }
9554         else
9555           break;
9556       }
9557     }
9558   }
9559 }
9560
9561 static void InitBuggyBase(int x, int y)
9562 {
9563   int element = Feld[x][y];
9564   int activating_delay = FRAMES_PER_SECOND / 4;
9565
9566   ChangeDelay[x][y] =
9567     (element == EL_SP_BUGGY_BASE ?
9568      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9569      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9570      activating_delay :
9571      element == EL_SP_BUGGY_BASE_ACTIVE ?
9572      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9573 }
9574
9575 static void WarnBuggyBase(int x, int y)
9576 {
9577   int i;
9578   static int xy[4][2] =
9579   {
9580     { 0, -1 },
9581     { -1, 0 },
9582     { +1, 0 },
9583     { 0, +1 }
9584   };
9585
9586   for (i = 0; i < NUM_DIRECTIONS; i++)
9587   {
9588     int xx = x + xy[i][0];
9589     int yy = y + xy[i][1];
9590
9591     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9592     {
9593       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9594
9595       break;
9596     }
9597   }
9598 }
9599
9600 static void InitTrap(int x, int y)
9601 {
9602   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9603 }
9604
9605 static void ActivateTrap(int x, int y)
9606 {
9607   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9608 }
9609
9610 static void ChangeActiveTrap(int x, int y)
9611 {
9612   int graphic = IMG_TRAP_ACTIVE;
9613
9614   /* if new animation frame was drawn, correct crumbled sand border */
9615   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9616     TEST_DrawLevelFieldCrumbled(x, y);
9617 }
9618
9619 static int getSpecialActionElement(int element, int number, int base_element)
9620 {
9621   return (element != EL_EMPTY ? element :
9622           number != -1 ? base_element + number - 1 :
9623           EL_EMPTY);
9624 }
9625
9626 static int getModifiedActionNumber(int value_old, int operator, int operand,
9627                                    int value_min, int value_max)
9628 {
9629   int value_new = (operator == CA_MODE_SET      ? operand :
9630                    operator == CA_MODE_ADD      ? value_old + operand :
9631                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9632                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9633                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9634                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9635                    value_old);
9636
9637   return (value_new < value_min ? value_min :
9638           value_new > value_max ? value_max :
9639           value_new);
9640 }
9641
9642 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9643 {
9644   struct ElementInfo *ei = &element_info[element];
9645   struct ElementChangeInfo *change = &ei->change_page[page];
9646   int target_element = change->target_element;
9647   int action_type = change->action_type;
9648   int action_mode = change->action_mode;
9649   int action_arg = change->action_arg;
9650   int action_element = change->action_element;
9651   int i;
9652
9653   if (!change->has_action)
9654     return;
9655
9656   /* ---------- determine action paramater values -------------------------- */
9657
9658   int level_time_value =
9659     (level.time > 0 ? TimeLeft :
9660      TimePlayed);
9661
9662   int action_arg_element_raw =
9663     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9664      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9665      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9666      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9667      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9668      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9669      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9670      EL_EMPTY);
9671   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9672
9673   int action_arg_direction =
9674     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9675      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9676      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9677      change->actual_trigger_side :
9678      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9679      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9680      MV_NONE);
9681
9682   int action_arg_number_min =
9683     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9684      CA_ARG_MIN);
9685
9686   int action_arg_number_max =
9687     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9688      action_type == CA_SET_LEVEL_GEMS ? 999 :
9689      action_type == CA_SET_LEVEL_TIME ? 9999 :
9690      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9691      action_type == CA_SET_CE_VALUE ? 9999 :
9692      action_type == CA_SET_CE_SCORE ? 9999 :
9693      CA_ARG_MAX);
9694
9695   int action_arg_number_reset =
9696     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9697      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9698      action_type == CA_SET_LEVEL_TIME ? level.time :
9699      action_type == CA_SET_LEVEL_SCORE ? 0 :
9700      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9701      action_type == CA_SET_CE_SCORE ? 0 :
9702      0);
9703
9704   int action_arg_number =
9705     (action_arg <= CA_ARG_MAX ? action_arg :
9706      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9707      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9708      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9709      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9710      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9711      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9712      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9713      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9714      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9715      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9716      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9717      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9718      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9719      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9720      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9721      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9722      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9723      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9724      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9725      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9726      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9727      -1);
9728
9729   int action_arg_number_old =
9730     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9731      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9732      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9733      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9734      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9735      0);
9736
9737   int action_arg_number_new =
9738     getModifiedActionNumber(action_arg_number_old,
9739                             action_mode, action_arg_number,
9740                             action_arg_number_min, action_arg_number_max);
9741
9742   int trigger_player_bits =
9743     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9744      change->actual_trigger_player_bits : change->trigger_player);
9745
9746   int action_arg_player_bits =
9747     (action_arg >= CA_ARG_PLAYER_1 &&
9748      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9749      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9750      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9751      PLAYER_BITS_ANY);
9752
9753   /* ---------- execute action  -------------------------------------------- */
9754
9755   switch (action_type)
9756   {
9757     case CA_NO_ACTION:
9758     {
9759       return;
9760     }
9761
9762     /* ---------- level actions  ------------------------------------------- */
9763
9764     case CA_RESTART_LEVEL:
9765     {
9766       game.restart_level = TRUE;
9767
9768       break;
9769     }
9770
9771     case CA_SHOW_ENVELOPE:
9772     {
9773       int element = getSpecialActionElement(action_arg_element,
9774                                             action_arg_number, EL_ENVELOPE_1);
9775
9776       if (IS_ENVELOPE(element))
9777         local_player->show_envelope = element;
9778
9779       break;
9780     }
9781
9782     case CA_SET_LEVEL_TIME:
9783     {
9784       if (level.time > 0)       /* only modify limited time value */
9785       {
9786         TimeLeft = action_arg_number_new;
9787
9788         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9789
9790         DisplayGameControlValues();
9791
9792         if (!TimeLeft && setup.time_limit)
9793           for (i = 0; i < MAX_PLAYERS; i++)
9794             KillPlayer(&stored_player[i]);
9795       }
9796
9797       break;
9798     }
9799
9800     case CA_SET_LEVEL_SCORE:
9801     {
9802       local_player->score = action_arg_number_new;
9803
9804       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9805
9806       DisplayGameControlValues();
9807
9808       break;
9809     }
9810
9811     case CA_SET_LEVEL_GEMS:
9812     {
9813       local_player->gems_still_needed = action_arg_number_new;
9814
9815       game.snapshot.collected_item = TRUE;
9816
9817       game_panel_controls[GAME_PANEL_GEMS].value =
9818         local_player->gems_still_needed;
9819
9820       DisplayGameControlValues();
9821
9822       break;
9823     }
9824
9825     case CA_SET_LEVEL_WIND:
9826     {
9827       game.wind_direction = action_arg_direction;
9828
9829       break;
9830     }
9831
9832     case CA_SET_LEVEL_RANDOM_SEED:
9833     {
9834       /* ensure that setting a new random seed while playing is predictable */
9835       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9836
9837       break;
9838     }
9839
9840     /* ---------- player actions  ------------------------------------------ */
9841
9842     case CA_MOVE_PLAYER:
9843     {
9844       /* automatically move to the next field in specified direction */
9845       for (i = 0; i < MAX_PLAYERS; i++)
9846         if (trigger_player_bits & (1 << i))
9847           stored_player[i].programmed_action = action_arg_direction;
9848
9849       break;
9850     }
9851
9852     case CA_EXIT_PLAYER:
9853     {
9854       for (i = 0; i < MAX_PLAYERS; i++)
9855         if (action_arg_player_bits & (1 << i))
9856           PlayerWins(&stored_player[i]);
9857
9858       break;
9859     }
9860
9861     case CA_KILL_PLAYER:
9862     {
9863       for (i = 0; i < MAX_PLAYERS; i++)
9864         if (action_arg_player_bits & (1 << i))
9865           KillPlayer(&stored_player[i]);
9866
9867       break;
9868     }
9869
9870     case CA_SET_PLAYER_KEYS:
9871     {
9872       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9873       int element = getSpecialActionElement(action_arg_element,
9874                                             action_arg_number, EL_KEY_1);
9875
9876       if (IS_KEY(element))
9877       {
9878         for (i = 0; i < MAX_PLAYERS; i++)
9879         {
9880           if (trigger_player_bits & (1 << i))
9881           {
9882             stored_player[i].key[KEY_NR(element)] = key_state;
9883
9884             DrawGameDoorValues();
9885           }
9886         }
9887       }
9888
9889       break;
9890     }
9891
9892     case CA_SET_PLAYER_SPEED:
9893     {
9894       for (i = 0; i < MAX_PLAYERS; i++)
9895       {
9896         if (trigger_player_bits & (1 << i))
9897         {
9898           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9899
9900           if (action_arg == CA_ARG_SPEED_FASTER &&
9901               stored_player[i].cannot_move)
9902           {
9903             action_arg_number = STEPSIZE_VERY_SLOW;
9904           }
9905           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9906                    action_arg == CA_ARG_SPEED_FASTER)
9907           {
9908             action_arg_number = 2;
9909             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9910                            CA_MODE_MULTIPLY);
9911           }
9912           else if (action_arg == CA_ARG_NUMBER_RESET)
9913           {
9914             action_arg_number = level.initial_player_stepsize[i];
9915           }
9916
9917           move_stepsize =
9918             getModifiedActionNumber(move_stepsize,
9919                                     action_mode,
9920                                     action_arg_number,
9921                                     action_arg_number_min,
9922                                     action_arg_number_max);
9923
9924           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9925         }
9926       }
9927
9928       break;
9929     }
9930
9931     case CA_SET_PLAYER_SHIELD:
9932     {
9933       for (i = 0; i < MAX_PLAYERS; i++)
9934       {
9935         if (trigger_player_bits & (1 << i))
9936         {
9937           if (action_arg == CA_ARG_SHIELD_OFF)
9938           {
9939             stored_player[i].shield_normal_time_left = 0;
9940             stored_player[i].shield_deadly_time_left = 0;
9941           }
9942           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9943           {
9944             stored_player[i].shield_normal_time_left = 999999;
9945           }
9946           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9947           {
9948             stored_player[i].shield_normal_time_left = 999999;
9949             stored_player[i].shield_deadly_time_left = 999999;
9950           }
9951         }
9952       }
9953
9954       break;
9955     }
9956
9957     case CA_SET_PLAYER_GRAVITY:
9958     {
9959       for (i = 0; i < MAX_PLAYERS; i++)
9960       {
9961         if (trigger_player_bits & (1 << i))
9962         {
9963           stored_player[i].gravity =
9964             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9965              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9966              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9967              stored_player[i].gravity);
9968         }
9969       }
9970
9971       break;
9972     }
9973
9974     case CA_SET_PLAYER_ARTWORK:
9975     {
9976       for (i = 0; i < MAX_PLAYERS; i++)
9977       {
9978         if (trigger_player_bits & (1 << i))
9979         {
9980           int artwork_element = action_arg_element;
9981
9982           if (action_arg == CA_ARG_ELEMENT_RESET)
9983             artwork_element =
9984               (level.use_artwork_element[i] ? level.artwork_element[i] :
9985                stored_player[i].element_nr);
9986
9987           if (stored_player[i].artwork_element != artwork_element)
9988             stored_player[i].Frame = 0;
9989
9990           stored_player[i].artwork_element = artwork_element;
9991
9992           SetPlayerWaiting(&stored_player[i], FALSE);
9993
9994           /* set number of special actions for bored and sleeping animation */
9995           stored_player[i].num_special_action_bored =
9996             get_num_special_action(artwork_element,
9997                                    ACTION_BORING_1, ACTION_BORING_LAST);
9998           stored_player[i].num_special_action_sleeping =
9999             get_num_special_action(artwork_element,
10000                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10001         }
10002       }
10003
10004       break;
10005     }
10006
10007     case CA_SET_PLAYER_INVENTORY:
10008     {
10009       for (i = 0; i < MAX_PLAYERS; i++)
10010       {
10011         struct PlayerInfo *player = &stored_player[i];
10012         int j, k;
10013
10014         if (trigger_player_bits & (1 << i))
10015         {
10016           int inventory_element = action_arg_element;
10017
10018           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10019               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10020               action_arg == CA_ARG_ELEMENT_ACTION)
10021           {
10022             int element = inventory_element;
10023             int collect_count = element_info[element].collect_count_initial;
10024
10025             if (!IS_CUSTOM_ELEMENT(element))
10026               collect_count = 1;
10027
10028             if (collect_count == 0)
10029               player->inventory_infinite_element = element;
10030             else
10031               for (k = 0; k < collect_count; k++)
10032                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10033                   player->inventory_element[player->inventory_size++] =
10034                     element;
10035           }
10036           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10037                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10038                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10039           {
10040             if (player->inventory_infinite_element != EL_UNDEFINED &&
10041                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10042                                      action_arg_element_raw))
10043               player->inventory_infinite_element = EL_UNDEFINED;
10044
10045             for (k = 0, j = 0; j < player->inventory_size; j++)
10046             {
10047               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10048                                         action_arg_element_raw))
10049                 player->inventory_element[k++] = player->inventory_element[j];
10050             }
10051
10052             player->inventory_size = k;
10053           }
10054           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10055           {
10056             if (player->inventory_size > 0)
10057             {
10058               for (j = 0; j < player->inventory_size - 1; j++)
10059                 player->inventory_element[j] = player->inventory_element[j + 1];
10060
10061               player->inventory_size--;
10062             }
10063           }
10064           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10065           {
10066             if (player->inventory_size > 0)
10067               player->inventory_size--;
10068           }
10069           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10070           {
10071             player->inventory_infinite_element = EL_UNDEFINED;
10072             player->inventory_size = 0;
10073           }
10074           else if (action_arg == CA_ARG_INVENTORY_RESET)
10075           {
10076             player->inventory_infinite_element = EL_UNDEFINED;
10077             player->inventory_size = 0;
10078
10079             if (level.use_initial_inventory[i])
10080             {
10081               for (j = 0; j < level.initial_inventory_size[i]; j++)
10082               {
10083                 int element = level.initial_inventory_content[i][j];
10084                 int collect_count = element_info[element].collect_count_initial;
10085
10086                 if (!IS_CUSTOM_ELEMENT(element))
10087                   collect_count = 1;
10088
10089                 if (collect_count == 0)
10090                   player->inventory_infinite_element = element;
10091                 else
10092                   for (k = 0; k < collect_count; k++)
10093                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10094                       player->inventory_element[player->inventory_size++] =
10095                         element;
10096               }
10097             }
10098           }
10099         }
10100       }
10101
10102       break;
10103     }
10104
10105     /* ---------- CE actions  ---------------------------------------------- */
10106
10107     case CA_SET_CE_VALUE:
10108     {
10109       int last_ce_value = CustomValue[x][y];
10110
10111       CustomValue[x][y] = action_arg_number_new;
10112
10113       if (CustomValue[x][y] != last_ce_value)
10114       {
10115         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10116         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10117
10118         if (CustomValue[x][y] == 0)
10119         {
10120           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10121           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10122         }
10123       }
10124
10125       break;
10126     }
10127
10128     case CA_SET_CE_SCORE:
10129     {
10130       int last_ce_score = ei->collect_score;
10131
10132       ei->collect_score = action_arg_number_new;
10133
10134       if (ei->collect_score != last_ce_score)
10135       {
10136         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10137         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10138
10139         if (ei->collect_score == 0)
10140         {
10141           int xx, yy;
10142
10143           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10144           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10145
10146           /*
10147             This is a very special case that seems to be a mixture between
10148             CheckElementChange() and CheckTriggeredElementChange(): while
10149             the first one only affects single elements that are triggered
10150             directly, the second one affects multiple elements in the playfield
10151             that are triggered indirectly by another element. This is a third
10152             case: Changing the CE score always affects multiple identical CEs,
10153             so every affected CE must be checked, not only the single CE for
10154             which the CE score was changed in the first place (as every instance
10155             of that CE shares the same CE score, and therefore also can change)!
10156           */
10157           SCAN_PLAYFIELD(xx, yy)
10158           {
10159             if (Feld[xx][yy] == element)
10160               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10161                                  CE_SCORE_GETS_ZERO);
10162           }
10163         }
10164       }
10165
10166       break;
10167     }
10168
10169     case CA_SET_CE_ARTWORK:
10170     {
10171       int artwork_element = action_arg_element;
10172       boolean reset_frame = FALSE;
10173       int xx, yy;
10174
10175       if (action_arg == CA_ARG_ELEMENT_RESET)
10176         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10177                            element);
10178
10179       if (ei->gfx_element != artwork_element)
10180         reset_frame = TRUE;
10181
10182       ei->gfx_element = artwork_element;
10183
10184       SCAN_PLAYFIELD(xx, yy)
10185       {
10186         if (Feld[xx][yy] == element)
10187         {
10188           if (reset_frame)
10189           {
10190             ResetGfxAnimation(xx, yy);
10191             ResetRandomAnimationValue(xx, yy);
10192           }
10193
10194           TEST_DrawLevelField(xx, yy);
10195         }
10196       }
10197
10198       break;
10199     }
10200
10201     /* ---------- engine actions  ------------------------------------------ */
10202
10203     case CA_SET_ENGINE_SCAN_MODE:
10204     {
10205       InitPlayfieldScanMode(action_arg);
10206
10207       break;
10208     }
10209
10210     default:
10211       break;
10212   }
10213 }
10214
10215 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10216 {
10217   int old_element = Feld[x][y];
10218   int new_element = GetElementFromGroupElement(element);
10219   int previous_move_direction = MovDir[x][y];
10220   int last_ce_value = CustomValue[x][y];
10221   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10222   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10223   boolean add_player_onto_element = (new_element_is_player &&
10224                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10225                                      IS_WALKABLE(old_element));
10226
10227   if (!add_player_onto_element)
10228   {
10229     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10230       RemoveMovingField(x, y);
10231     else
10232       RemoveField(x, y);
10233
10234     Feld[x][y] = new_element;
10235
10236     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10237       MovDir[x][y] = previous_move_direction;
10238
10239     if (element_info[new_element].use_last_ce_value)
10240       CustomValue[x][y] = last_ce_value;
10241
10242     InitField_WithBug1(x, y, FALSE);
10243
10244     new_element = Feld[x][y];   /* element may have changed */
10245
10246     ResetGfxAnimation(x, y);
10247     ResetRandomAnimationValue(x, y);
10248
10249     TEST_DrawLevelField(x, y);
10250
10251     if (GFX_CRUMBLED(new_element))
10252       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10253   }
10254
10255   /* check if element under the player changes from accessible to unaccessible
10256      (needed for special case of dropping element which then changes) */
10257   /* (must be checked after creating new element for walkable group elements) */
10258   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10259       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10260   {
10261     Bang(x, y);
10262
10263     return;
10264   }
10265
10266   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10267   if (new_element_is_player)
10268     RelocatePlayer(x, y, new_element);
10269
10270   if (is_change)
10271     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10272
10273   TestIfBadThingTouchesPlayer(x, y);
10274   TestIfPlayerTouchesCustomElement(x, y);
10275   TestIfElementTouchesCustomElement(x, y);
10276 }
10277
10278 static void CreateField(int x, int y, int element)
10279 {
10280   CreateFieldExt(x, y, element, FALSE);
10281 }
10282
10283 static void CreateElementFromChange(int x, int y, int element)
10284 {
10285   element = GET_VALID_RUNTIME_ELEMENT(element);
10286
10287   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10288   {
10289     int old_element = Feld[x][y];
10290
10291     /* prevent changed element from moving in same engine frame
10292        unless both old and new element can either fall or move */
10293     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10294         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10295       Stop[x][y] = TRUE;
10296   }
10297
10298   CreateFieldExt(x, y, element, TRUE);
10299 }
10300
10301 static boolean ChangeElement(int x, int y, int element, int page)
10302 {
10303   struct ElementInfo *ei = &element_info[element];
10304   struct ElementChangeInfo *change = &ei->change_page[page];
10305   int ce_value = CustomValue[x][y];
10306   int ce_score = ei->collect_score;
10307   int target_element;
10308   int old_element = Feld[x][y];
10309
10310   /* always use default change event to prevent running into a loop */
10311   if (ChangeEvent[x][y] == -1)
10312     ChangeEvent[x][y] = CE_DELAY;
10313
10314   if (ChangeEvent[x][y] == CE_DELAY)
10315   {
10316     /* reset actual trigger element, trigger player and action element */
10317     change->actual_trigger_element = EL_EMPTY;
10318     change->actual_trigger_player = EL_EMPTY;
10319     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10320     change->actual_trigger_side = CH_SIDE_NONE;
10321     change->actual_trigger_ce_value = 0;
10322     change->actual_trigger_ce_score = 0;
10323   }
10324
10325   /* do not change elements more than a specified maximum number of changes */
10326   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10327     return FALSE;
10328
10329   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10330
10331   if (change->explode)
10332   {
10333     Bang(x, y);
10334
10335     return TRUE;
10336   }
10337
10338   if (change->use_target_content)
10339   {
10340     boolean complete_replace = TRUE;
10341     boolean can_replace[3][3];
10342     int xx, yy;
10343
10344     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10345     {
10346       boolean is_empty;
10347       boolean is_walkable;
10348       boolean is_diggable;
10349       boolean is_collectible;
10350       boolean is_removable;
10351       boolean is_destructible;
10352       int ex = x + xx - 1;
10353       int ey = y + yy - 1;
10354       int content_element = change->target_content.e[xx][yy];
10355       int e;
10356
10357       can_replace[xx][yy] = TRUE;
10358
10359       if (ex == x && ey == y)   /* do not check changing element itself */
10360         continue;
10361
10362       if (content_element == EL_EMPTY_SPACE)
10363       {
10364         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10365
10366         continue;
10367       }
10368
10369       if (!IN_LEV_FIELD(ex, ey))
10370       {
10371         can_replace[xx][yy] = FALSE;
10372         complete_replace = FALSE;
10373
10374         continue;
10375       }
10376
10377       e = Feld[ex][ey];
10378
10379       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10380         e = MovingOrBlocked2Element(ex, ey);
10381
10382       is_empty = (IS_FREE(ex, ey) ||
10383                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10384
10385       is_walkable     = (is_empty || IS_WALKABLE(e));
10386       is_diggable     = (is_empty || IS_DIGGABLE(e));
10387       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10388       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10389       is_removable    = (is_diggable || is_collectible);
10390
10391       can_replace[xx][yy] =
10392         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10393           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10394           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10395           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10396           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10397           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10398          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10399
10400       if (!can_replace[xx][yy])
10401         complete_replace = FALSE;
10402     }
10403
10404     if (!change->only_if_complete || complete_replace)
10405     {
10406       boolean something_has_changed = FALSE;
10407
10408       if (change->only_if_complete && change->use_random_replace &&
10409           RND(100) < change->random_percentage)
10410         return FALSE;
10411
10412       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10413       {
10414         int ex = x + xx - 1;
10415         int ey = y + yy - 1;
10416         int content_element;
10417
10418         if (can_replace[xx][yy] && (!change->use_random_replace ||
10419                                     RND(100) < change->random_percentage))
10420         {
10421           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10422             RemoveMovingField(ex, ey);
10423
10424           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10425
10426           content_element = change->target_content.e[xx][yy];
10427           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10428                                               ce_value, ce_score);
10429
10430           CreateElementFromChange(ex, ey, target_element);
10431
10432           something_has_changed = TRUE;
10433
10434           /* for symmetry reasons, freeze newly created border elements */
10435           if (ex != x || ey != y)
10436             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10437         }
10438       }
10439
10440       if (something_has_changed)
10441       {
10442         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10443         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10444       }
10445     }
10446   }
10447   else
10448   {
10449     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10450                                         ce_value, ce_score);
10451
10452     if (element == EL_DIAGONAL_GROWING ||
10453         element == EL_DIAGONAL_SHRINKING)
10454     {
10455       target_element = Store[x][y];
10456
10457       Store[x][y] = EL_EMPTY;
10458     }
10459
10460     CreateElementFromChange(x, y, target_element);
10461
10462     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10463     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10464   }
10465
10466   /* this uses direct change before indirect change */
10467   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10468
10469   return TRUE;
10470 }
10471
10472 static void HandleElementChange(int x, int y, int page)
10473 {
10474   int element = MovingOrBlocked2Element(x, y);
10475   struct ElementInfo *ei = &element_info[element];
10476   struct ElementChangeInfo *change = &ei->change_page[page];
10477   boolean handle_action_before_change = FALSE;
10478
10479 #ifdef DEBUG
10480   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10481       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10482   {
10483     printf("\n\n");
10484     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10485            x, y, element, element_info[element].token_name);
10486     printf("HandleElementChange(): This should never happen!\n");
10487     printf("\n\n");
10488   }
10489 #endif
10490
10491   /* this can happen with classic bombs on walkable, changing elements */
10492   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10493   {
10494     return;
10495   }
10496
10497   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10498   {
10499     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10500
10501     if (change->can_change)
10502     {
10503       /* !!! not clear why graphic animation should be reset at all here !!! */
10504       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10505       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10506
10507       /*
10508         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10509
10510         When using an animation frame delay of 1 (this only happens with
10511         "sp_zonk.moving.left/right" in the classic graphics), the default
10512         (non-moving) animation shows wrong animation frames (while the
10513         moving animation, like "sp_zonk.moving.left/right", is correct,
10514         so this graphical bug never shows up with the classic graphics).
10515         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10516         be drawn instead of the correct frames 0,1,2,3. This is caused by
10517         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10518         an element change: First when the change delay ("ChangeDelay[][]")
10519         counter has reached zero after decrementing, then a second time in
10520         the next frame (after "GfxFrame[][]" was already incremented) when
10521         "ChangeDelay[][]" is reset to the initial delay value again.
10522
10523         This causes frame 0 to be drawn twice, while the last frame won't
10524         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10525
10526         As some animations may already be cleverly designed around this bug
10527         (at least the "Snake Bite" snake tail animation does this), it cannot
10528         simply be fixed here without breaking such existing animations.
10529         Unfortunately, it cannot easily be detected if a graphics set was
10530         designed "before" or "after" the bug was fixed. As a workaround,
10531         a new graphics set option "game.graphics_engine_version" was added
10532         to be able to specify the game's major release version for which the
10533         graphics set was designed, which can then be used to decide if the
10534         bugfix should be used (version 4 and above) or not (version 3 or
10535         below, or if no version was specified at all, as with old sets).
10536
10537         (The wrong/fixed animation frames can be tested with the test level set
10538         "test_gfxframe" and level "000", which contains a specially prepared
10539         custom element at level position (x/y) == (11/9) which uses the zonk
10540         animation mentioned above. Using "game.graphics_engine_version: 4"
10541         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10542         This can also be seen from the debug output for this test element.)
10543       */
10544
10545       /* when a custom element is about to change (for example by change delay),
10546          do not reset graphic animation when the custom element is moving */
10547       if (game.graphics_engine_version < 4 &&
10548           !IS_MOVING(x, y))
10549       {
10550         ResetGfxAnimation(x, y);
10551         ResetRandomAnimationValue(x, y);
10552       }
10553
10554       if (change->pre_change_function)
10555         change->pre_change_function(x, y);
10556     }
10557   }
10558
10559   ChangeDelay[x][y]--;
10560
10561   if (ChangeDelay[x][y] != 0)           /* continue element change */
10562   {
10563     if (change->can_change)
10564     {
10565       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10566
10567       if (IS_ANIMATED(graphic))
10568         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10569
10570       if (change->change_function)
10571         change->change_function(x, y);
10572     }
10573   }
10574   else                                  /* finish element change */
10575   {
10576     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10577     {
10578       page = ChangePage[x][y];
10579       ChangePage[x][y] = -1;
10580
10581       change = &ei->change_page[page];
10582     }
10583
10584     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10585     {
10586       ChangeDelay[x][y] = 1;            /* try change after next move step */
10587       ChangePage[x][y] = page;          /* remember page to use for change */
10588
10589       return;
10590     }
10591
10592     /* special case: set new level random seed before changing element */
10593     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10594       handle_action_before_change = TRUE;
10595
10596     if (change->has_action && handle_action_before_change)
10597       ExecuteCustomElementAction(x, y, element, page);
10598
10599     if (change->can_change)
10600     {
10601       if (ChangeElement(x, y, element, page))
10602       {
10603         if (change->post_change_function)
10604           change->post_change_function(x, y);
10605       }
10606     }
10607
10608     if (change->has_action && !handle_action_before_change)
10609       ExecuteCustomElementAction(x, y, element, page);
10610   }
10611 }
10612
10613 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10614                                               int trigger_element,
10615                                               int trigger_event,
10616                                               int trigger_player,
10617                                               int trigger_side,
10618                                               int trigger_page)
10619 {
10620   boolean change_done_any = FALSE;
10621   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10622   int i;
10623
10624   if (!(trigger_events[trigger_element][trigger_event]))
10625     return FALSE;
10626
10627   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10628
10629   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10630   {
10631     int element = EL_CUSTOM_START + i;
10632     boolean change_done = FALSE;
10633     int p;
10634
10635     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10636         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10637       continue;
10638
10639     for (p = 0; p < element_info[element].num_change_pages; p++)
10640     {
10641       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10642
10643       if (change->can_change_or_has_action &&
10644           change->has_event[trigger_event] &&
10645           change->trigger_side & trigger_side &&
10646           change->trigger_player & trigger_player &&
10647           change->trigger_page & trigger_page_bits &&
10648           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10649       {
10650         change->actual_trigger_element = trigger_element;
10651         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10652         change->actual_trigger_player_bits = trigger_player;
10653         change->actual_trigger_side = trigger_side;
10654         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10655         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10656
10657         if ((change->can_change && !change_done) || change->has_action)
10658         {
10659           int x, y;
10660
10661           SCAN_PLAYFIELD(x, y)
10662           {
10663             if (Feld[x][y] == element)
10664             {
10665               if (change->can_change && !change_done)
10666               {
10667                 /* if element already changed in this frame, not only prevent
10668                    another element change (checked in ChangeElement()), but
10669                    also prevent additional element actions for this element */
10670
10671                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10672                     !level.use_action_after_change_bug)
10673                   continue;
10674
10675                 ChangeDelay[x][y] = 1;
10676                 ChangeEvent[x][y] = trigger_event;
10677
10678                 HandleElementChange(x, y, p);
10679               }
10680               else if (change->has_action)
10681               {
10682                 /* if element already changed in this frame, not only prevent
10683                    another element change (checked in ChangeElement()), but
10684                    also prevent additional element actions for this element */
10685
10686                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10687                     !level.use_action_after_change_bug)
10688                   continue;
10689
10690                 ExecuteCustomElementAction(x, y, element, p);
10691                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10692               }
10693             }
10694           }
10695
10696           if (change->can_change)
10697           {
10698             change_done = TRUE;
10699             change_done_any = TRUE;
10700           }
10701         }
10702       }
10703     }
10704   }
10705
10706   RECURSION_LOOP_DETECTION_END();
10707
10708   return change_done_any;
10709 }
10710
10711 static boolean CheckElementChangeExt(int x, int y,
10712                                      int element,
10713                                      int trigger_element,
10714                                      int trigger_event,
10715                                      int trigger_player,
10716                                      int trigger_side)
10717 {
10718   boolean change_done = FALSE;
10719   int p;
10720
10721   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10722       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10723     return FALSE;
10724
10725   if (Feld[x][y] == EL_BLOCKED)
10726   {
10727     Blocked2Moving(x, y, &x, &y);
10728     element = Feld[x][y];
10729   }
10730
10731   /* check if element has already changed or is about to change after moving */
10732   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10733        Feld[x][y] != element) ||
10734
10735       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10736        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10737         ChangePage[x][y] != -1)))
10738     return FALSE;
10739
10740   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10741
10742   for (p = 0; p < element_info[element].num_change_pages; p++)
10743   {
10744     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10745
10746     /* check trigger element for all events where the element that is checked
10747        for changing interacts with a directly adjacent element -- this is
10748        different to element changes that affect other elements to change on the
10749        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10750     boolean check_trigger_element =
10751       (trigger_event == CE_TOUCHING_X ||
10752        trigger_event == CE_HITTING_X ||
10753        trigger_event == CE_HIT_BY_X ||
10754        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10755
10756     if (change->can_change_or_has_action &&
10757         change->has_event[trigger_event] &&
10758         change->trigger_side & trigger_side &&
10759         change->trigger_player & trigger_player &&
10760         (!check_trigger_element ||
10761          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10762     {
10763       change->actual_trigger_element = trigger_element;
10764       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10765       change->actual_trigger_player_bits = trigger_player;
10766       change->actual_trigger_side = trigger_side;
10767       change->actual_trigger_ce_value = CustomValue[x][y];
10768       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10769
10770       /* special case: trigger element not at (x,y) position for some events */
10771       if (check_trigger_element)
10772       {
10773         static struct
10774         {
10775           int dx, dy;
10776         } move_xy[] =
10777           {
10778             {  0,  0 },
10779             { -1,  0 },
10780             { +1,  0 },
10781             {  0,  0 },
10782             {  0, -1 },
10783             {  0,  0 }, { 0, 0 }, { 0, 0 },
10784             {  0, +1 }
10785           };
10786
10787         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10788         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10789
10790         change->actual_trigger_ce_value = CustomValue[xx][yy];
10791         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10792       }
10793
10794       if (change->can_change && !change_done)
10795       {
10796         ChangeDelay[x][y] = 1;
10797         ChangeEvent[x][y] = trigger_event;
10798
10799         HandleElementChange(x, y, p);
10800
10801         change_done = TRUE;
10802       }
10803       else if (change->has_action)
10804       {
10805         ExecuteCustomElementAction(x, y, element, p);
10806         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10807       }
10808     }
10809   }
10810
10811   RECURSION_LOOP_DETECTION_END();
10812
10813   return change_done;
10814 }
10815
10816 static void PlayPlayerSound(struct PlayerInfo *player)
10817 {
10818   int jx = player->jx, jy = player->jy;
10819   int sound_element = player->artwork_element;
10820   int last_action = player->last_action_waiting;
10821   int action = player->action_waiting;
10822
10823   if (player->is_waiting)
10824   {
10825     if (action != last_action)
10826       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10827     else
10828       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10829   }
10830   else
10831   {
10832     if (action != last_action)
10833       StopSound(element_info[sound_element].sound[last_action]);
10834
10835     if (last_action == ACTION_SLEEPING)
10836       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10837   }
10838 }
10839
10840 static void PlayAllPlayersSound()
10841 {
10842   int i;
10843
10844   for (i = 0; i < MAX_PLAYERS; i++)
10845     if (stored_player[i].active)
10846       PlayPlayerSound(&stored_player[i]);
10847 }
10848
10849 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10850 {
10851   boolean last_waiting = player->is_waiting;
10852   int move_dir = player->MovDir;
10853
10854   player->dir_waiting = move_dir;
10855   player->last_action_waiting = player->action_waiting;
10856
10857   if (is_waiting)
10858   {
10859     if (!last_waiting)          /* not waiting -> waiting */
10860     {
10861       player->is_waiting = TRUE;
10862
10863       player->frame_counter_bored =
10864         FrameCounter +
10865         game.player_boring_delay_fixed +
10866         GetSimpleRandom(game.player_boring_delay_random);
10867       player->frame_counter_sleeping =
10868         FrameCounter +
10869         game.player_sleeping_delay_fixed +
10870         GetSimpleRandom(game.player_sleeping_delay_random);
10871
10872       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10873     }
10874
10875     if (game.player_sleeping_delay_fixed +
10876         game.player_sleeping_delay_random > 0 &&
10877         player->anim_delay_counter == 0 &&
10878         player->post_delay_counter == 0 &&
10879         FrameCounter >= player->frame_counter_sleeping)
10880       player->is_sleeping = TRUE;
10881     else if (game.player_boring_delay_fixed +
10882              game.player_boring_delay_random > 0 &&
10883              FrameCounter >= player->frame_counter_bored)
10884       player->is_bored = TRUE;
10885
10886     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10887                               player->is_bored ? ACTION_BORING :
10888                               ACTION_WAITING);
10889
10890     if (player->is_sleeping && player->use_murphy)
10891     {
10892       /* special case for sleeping Murphy when leaning against non-free tile */
10893
10894       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10895           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10896            !IS_MOVING(player->jx - 1, player->jy)))
10897         move_dir = MV_LEFT;
10898       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10899                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10900                 !IS_MOVING(player->jx + 1, player->jy)))
10901         move_dir = MV_RIGHT;
10902       else
10903         player->is_sleeping = FALSE;
10904
10905       player->dir_waiting = move_dir;
10906     }
10907
10908     if (player->is_sleeping)
10909     {
10910       if (player->num_special_action_sleeping > 0)
10911       {
10912         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10913         {
10914           int last_special_action = player->special_action_sleeping;
10915           int num_special_action = player->num_special_action_sleeping;
10916           int special_action =
10917             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10918              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10919              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10920              last_special_action + 1 : ACTION_SLEEPING);
10921           int special_graphic =
10922             el_act_dir2img(player->artwork_element, special_action, move_dir);
10923
10924           player->anim_delay_counter =
10925             graphic_info[special_graphic].anim_delay_fixed +
10926             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10927           player->post_delay_counter =
10928             graphic_info[special_graphic].post_delay_fixed +
10929             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10930
10931           player->special_action_sleeping = special_action;
10932         }
10933
10934         if (player->anim_delay_counter > 0)
10935         {
10936           player->action_waiting = player->special_action_sleeping;
10937           player->anim_delay_counter--;
10938         }
10939         else if (player->post_delay_counter > 0)
10940         {
10941           player->post_delay_counter--;
10942         }
10943       }
10944     }
10945     else if (player->is_bored)
10946     {
10947       if (player->num_special_action_bored > 0)
10948       {
10949         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10950         {
10951           int special_action =
10952             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10953           int special_graphic =
10954             el_act_dir2img(player->artwork_element, special_action, move_dir);
10955
10956           player->anim_delay_counter =
10957             graphic_info[special_graphic].anim_delay_fixed +
10958             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10959           player->post_delay_counter =
10960             graphic_info[special_graphic].post_delay_fixed +
10961             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10962
10963           player->special_action_bored = special_action;
10964         }
10965
10966         if (player->anim_delay_counter > 0)
10967         {
10968           player->action_waiting = player->special_action_bored;
10969           player->anim_delay_counter--;
10970         }
10971         else if (player->post_delay_counter > 0)
10972         {
10973           player->post_delay_counter--;
10974         }
10975       }
10976     }
10977   }
10978   else if (last_waiting)        /* waiting -> not waiting */
10979   {
10980     player->is_waiting = FALSE;
10981     player->is_bored = FALSE;
10982     player->is_sleeping = FALSE;
10983
10984     player->frame_counter_bored = -1;
10985     player->frame_counter_sleeping = -1;
10986
10987     player->anim_delay_counter = 0;
10988     player->post_delay_counter = 0;
10989
10990     player->dir_waiting = player->MovDir;
10991     player->action_waiting = ACTION_DEFAULT;
10992
10993     player->special_action_bored = ACTION_DEFAULT;
10994     player->special_action_sleeping = ACTION_DEFAULT;
10995   }
10996 }
10997
10998 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10999 {
11000   if ((!player->is_moving  && player->was_moving) ||
11001       (player->MovPos == 0 && player->was_moving) ||
11002       (player->is_snapping && !player->was_snapping) ||
11003       (player->is_dropping && !player->was_dropping))
11004   {
11005     if (!CheckSaveEngineSnapshotToList())
11006       return;
11007
11008     player->was_moving = FALSE;
11009     player->was_snapping = TRUE;
11010     player->was_dropping = TRUE;
11011   }
11012   else
11013   {
11014     if (player->is_moving)
11015       player->was_moving = TRUE;
11016
11017     if (!player->is_snapping)
11018       player->was_snapping = FALSE;
11019
11020     if (!player->is_dropping)
11021       player->was_dropping = FALSE;
11022   }
11023 }
11024
11025 static void CheckSingleStepMode(struct PlayerInfo *player)
11026 {
11027   if (tape.single_step && tape.recording && !tape.pausing)
11028   {
11029     /* as it is called "single step mode", just return to pause mode when the
11030        player stopped moving after one tile (or never starts moving at all) */
11031     if (!player->is_moving &&
11032         !player->is_pushing &&
11033         !player->is_dropping_pressed)
11034     {
11035       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11036       SnapField(player, 0, 0);                  /* stop snapping */
11037     }
11038   }
11039
11040   CheckSaveEngineSnapshot(player);
11041 }
11042
11043 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11044 {
11045   int left      = player_action & JOY_LEFT;
11046   int right     = player_action & JOY_RIGHT;
11047   int up        = player_action & JOY_UP;
11048   int down      = player_action & JOY_DOWN;
11049   int button1   = player_action & JOY_BUTTON_1;
11050   int button2   = player_action & JOY_BUTTON_2;
11051   int dx        = (left ? -1 : right ? 1 : 0);
11052   int dy        = (up   ? -1 : down  ? 1 : 0);
11053
11054   if (!player->active || tape.pausing)
11055     return 0;
11056
11057   if (player_action)
11058   {
11059     if (button1)
11060       SnapField(player, dx, dy);
11061     else
11062     {
11063       if (button2)
11064         DropElement(player);
11065
11066       MovePlayer(player, dx, dy);
11067     }
11068
11069     CheckSingleStepMode(player);
11070
11071     SetPlayerWaiting(player, FALSE);
11072
11073     return player_action;
11074   }
11075   else
11076   {
11077     /* no actions for this player (no input at player's configured device) */
11078
11079     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11080     SnapField(player, 0, 0);
11081     CheckGravityMovementWhenNotMoving(player);
11082
11083     if (player->MovPos == 0)
11084       SetPlayerWaiting(player, TRUE);
11085
11086     if (player->MovPos == 0)    /* needed for tape.playing */
11087       player->is_moving = FALSE;
11088
11089     player->is_dropping = FALSE;
11090     player->is_dropping_pressed = FALSE;
11091     player->drop_pressed_delay = 0;
11092
11093     CheckSingleStepMode(player);
11094
11095     return 0;
11096   }
11097 }
11098
11099 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11100                                          byte *tape_action)
11101 {
11102   if (!tape.use_mouse)
11103     return;
11104
11105   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11106   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11107   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11108 }
11109
11110 static void SetTapeActionFromMouseAction(byte *tape_action,
11111                                          struct MouseActionInfo *mouse_action)
11112 {
11113   if (!tape.use_mouse)
11114     return;
11115
11116   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11117   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11118   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11119 }
11120
11121 static void CheckLevelTime()
11122 {
11123   int i;
11124
11125   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11126   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11127   {
11128     if (level.native_em_level->lev->home == 0)  /* all players at home */
11129     {
11130       PlayerWins(local_player);
11131
11132       AllPlayersGone = TRUE;
11133
11134       level.native_em_level->lev->home = -1;
11135     }
11136
11137     if (level.native_em_level->ply[0]->alive == 0 &&
11138         level.native_em_level->ply[1]->alive == 0 &&
11139         level.native_em_level->ply[2]->alive == 0 &&
11140         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11141       AllPlayersGone = TRUE;
11142   }
11143   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11144   {
11145     if (game_sp.LevelSolved &&
11146         !game_sp.GameOver)                              /* game won */
11147     {
11148       PlayerWins(local_player);
11149
11150       game_sp.GameOver = TRUE;
11151
11152       AllPlayersGone = TRUE;
11153     }
11154
11155     if (game_sp.GameOver)                               /* game lost */
11156       AllPlayersGone = TRUE;
11157   }
11158   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11159   {
11160     if (game_mm.level_solved &&
11161         !game_mm.game_over)                             /* game won */
11162     {
11163       PlayerWins(local_player);
11164
11165       game_mm.game_over = TRUE;
11166
11167       AllPlayersGone = TRUE;
11168     }
11169
11170     if (game_mm.game_over)                              /* game lost */
11171       AllPlayersGone = TRUE;
11172   }
11173
11174   if (TimeFrames >= FRAMES_PER_SECOND)
11175   {
11176     TimeFrames = 0;
11177     TapeTime++;
11178
11179     for (i = 0; i < MAX_PLAYERS; i++)
11180     {
11181       struct PlayerInfo *player = &stored_player[i];
11182
11183       if (SHIELD_ON(player))
11184       {
11185         player->shield_normal_time_left--;
11186
11187         if (player->shield_deadly_time_left > 0)
11188           player->shield_deadly_time_left--;
11189       }
11190     }
11191
11192     if (!local_player->LevelSolved && !level.use_step_counter)
11193     {
11194       TimePlayed++;
11195
11196       if (TimeLeft > 0)
11197       {
11198         TimeLeft--;
11199
11200         if (TimeLeft <= 10 && setup.time_limit)
11201           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11202
11203         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11204            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11205
11206         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11207
11208         if (!TimeLeft && setup.time_limit)
11209         {
11210           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11211             level.native_em_level->lev->killed_out_of_time = TRUE;
11212           else
11213             for (i = 0; i < MAX_PLAYERS; i++)
11214               KillPlayer(&stored_player[i]);
11215         }
11216       }
11217       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11218       {
11219         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11220       }
11221
11222       level.native_em_level->lev->time =
11223         (game.no_time_limit ? TimePlayed : TimeLeft);
11224     }
11225
11226     if (tape.recording || tape.playing)
11227       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11228   }
11229
11230   if (tape.recording || tape.playing)
11231     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11232
11233   UpdateAndDisplayGameControlValues();
11234 }
11235
11236 void AdvanceFrameAndPlayerCounters(int player_nr)
11237 {
11238   int i;
11239
11240   /* advance frame counters (global frame counter and time frame counter) */
11241   FrameCounter++;
11242   TimeFrames++;
11243
11244   /* advance player counters (counters for move delay, move animation etc.) */
11245   for (i = 0; i < MAX_PLAYERS; i++)
11246   {
11247     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11248     int move_delay_value = stored_player[i].move_delay_value;
11249     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11250
11251     if (!advance_player_counters)       /* not all players may be affected */
11252       continue;
11253
11254     if (move_frames == 0)       /* less than one move per game frame */
11255     {
11256       int stepsize = TILEX / move_delay_value;
11257       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11258       int count = (stored_player[i].is_moving ?
11259                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11260
11261       if (count % delay == 0)
11262         move_frames = 1;
11263     }
11264
11265     stored_player[i].Frame += move_frames;
11266
11267     if (stored_player[i].MovPos != 0)
11268       stored_player[i].StepFrame += move_frames;
11269
11270     if (stored_player[i].move_delay > 0)
11271       stored_player[i].move_delay--;
11272
11273     /* due to bugs in previous versions, counter must count up, not down */
11274     if (stored_player[i].push_delay != -1)
11275       stored_player[i].push_delay++;
11276
11277     if (stored_player[i].drop_delay > 0)
11278       stored_player[i].drop_delay--;
11279
11280     if (stored_player[i].is_dropping_pressed)
11281       stored_player[i].drop_pressed_delay++;
11282   }
11283 }
11284
11285 void StartGameActions(boolean init_network_game, boolean record_tape,
11286                       int random_seed)
11287 {
11288   unsigned int new_random_seed = InitRND(random_seed);
11289
11290   if (record_tape)
11291     TapeStartRecording(new_random_seed);
11292
11293 #if defined(NETWORK_AVALIABLE)
11294   if (init_network_game)
11295   {
11296     SendToServer_StartPlaying();
11297
11298     return;
11299   }
11300 #endif
11301
11302   InitGame();
11303 }
11304
11305 void GameActionsExt()
11306 {
11307 #if 0
11308   static unsigned int game_frame_delay = 0;
11309 #endif
11310   unsigned int game_frame_delay_value;
11311   byte *recorded_player_action;
11312   byte summarized_player_action = 0;
11313   byte tape_action[MAX_PLAYERS];
11314   int i;
11315
11316   /* detect endless loops, caused by custom element programming */
11317   if (recursion_loop_detected && recursion_loop_depth == 0)
11318   {
11319     char *message = getStringCat3("Internal Error! Element ",
11320                                   EL_NAME(recursion_loop_element),
11321                                   " caused endless loop! Quit the game?");
11322
11323     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11324           EL_NAME(recursion_loop_element));
11325
11326     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11327
11328     recursion_loop_detected = FALSE;    /* if game should be continued */
11329
11330     free(message);
11331
11332     return;
11333   }
11334
11335   if (game.restart_level)
11336     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11337
11338   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11339   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11340   {
11341     if (level.native_em_level->lev->home == 0)  /* all players at home */
11342     {
11343       PlayerWins(local_player);
11344
11345       AllPlayersGone = TRUE;
11346
11347       level.native_em_level->lev->home = -1;
11348     }
11349
11350     if (level.native_em_level->ply[0]->alive == 0 &&
11351         level.native_em_level->ply[1]->alive == 0 &&
11352         level.native_em_level->ply[2]->alive == 0 &&
11353         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11354       AllPlayersGone = TRUE;
11355   }
11356   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11357   {
11358     if (game_sp.LevelSolved &&
11359         !game_sp.GameOver)                              /* game won */
11360     {
11361       PlayerWins(local_player);
11362
11363       game_sp.GameOver = TRUE;
11364
11365       AllPlayersGone = TRUE;
11366     }
11367
11368     if (game_sp.GameOver)                               /* game lost */
11369       AllPlayersGone = TRUE;
11370   }
11371   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11372   {
11373     if (game_mm.level_solved &&
11374         !game_mm.game_over)                             /* game won */
11375     {
11376       PlayerWins(local_player);
11377
11378       game_mm.game_over = TRUE;
11379
11380       AllPlayersGone = TRUE;
11381     }
11382
11383     if (game_mm.game_over)                              /* game lost */
11384       AllPlayersGone = TRUE;
11385   }
11386
11387   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11388     GameWon();
11389
11390   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11391     TapeStop();
11392
11393   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11394     return;
11395
11396   game_frame_delay_value =
11397     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11398
11399   if (tape.playing && tape.warp_forward && !tape.pausing)
11400     game_frame_delay_value = 0;
11401
11402   SetVideoFrameDelay(game_frame_delay_value);
11403
11404 #if 0
11405 #if 0
11406   /* ---------- main game synchronization point ---------- */
11407
11408   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11409
11410   printf("::: skip == %d\n", skip);
11411
11412 #else
11413   /* ---------- main game synchronization point ---------- */
11414
11415   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11416 #endif
11417 #endif
11418
11419   if (network_playing && !network_player_action_received)
11420   {
11421     /* try to get network player actions in time */
11422
11423 #if defined(NETWORK_AVALIABLE)
11424     /* last chance to get network player actions without main loop delay */
11425     HandleNetworking();
11426 #endif
11427
11428     /* game was quit by network peer */
11429     if (game_status != GAME_MODE_PLAYING)
11430       return;
11431
11432     if (!network_player_action_received)
11433       return;           /* failed to get network player actions in time */
11434
11435     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11436   }
11437
11438   if (tape.pausing)
11439     return;
11440
11441   /* at this point we know that we really continue executing the game */
11442
11443   network_player_action_received = FALSE;
11444
11445   /* when playing tape, read previously recorded player input from tape data */
11446   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11447
11448   local_player->effective_mouse_action = local_player->mouse_action;
11449
11450   if (recorded_player_action != NULL)
11451     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11452                                  recorded_player_action);
11453
11454   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11455   if (tape.pausing)
11456     return;
11457
11458   if (tape.set_centered_player)
11459   {
11460     game.centered_player_nr_next = tape.centered_player_nr_next;
11461     game.set_centered_player = TRUE;
11462   }
11463
11464   for (i = 0; i < MAX_PLAYERS; i++)
11465   {
11466     summarized_player_action |= stored_player[i].action;
11467
11468     if (!network_playing && (game.team_mode || tape.playing))
11469       stored_player[i].effective_action = stored_player[i].action;
11470   }
11471
11472 #if defined(NETWORK_AVALIABLE)
11473   if (network_playing)
11474     SendToServer_MovePlayer(summarized_player_action);
11475 #endif
11476
11477   // summarize all actions at local players mapped input device position
11478   // (this allows using different input devices in single player mode)
11479   if (!network.enabled && !game.team_mode)
11480     stored_player[map_player_action[local_player->index_nr]].effective_action =
11481       summarized_player_action;
11482
11483   if (tape.recording &&
11484       setup.team_mode &&
11485       setup.input_on_focus &&
11486       game.centered_player_nr != -1)
11487   {
11488     for (i = 0; i < MAX_PLAYERS; i++)
11489       stored_player[i].effective_action =
11490         (i == game.centered_player_nr ? summarized_player_action : 0);
11491   }
11492
11493   if (recorded_player_action != NULL)
11494     for (i = 0; i < MAX_PLAYERS; i++)
11495       stored_player[i].effective_action = recorded_player_action[i];
11496
11497   for (i = 0; i < MAX_PLAYERS; i++)
11498   {
11499     tape_action[i] = stored_player[i].effective_action;
11500
11501     /* (this may happen in the RND game engine if a player was not present on
11502        the playfield on level start, but appeared later from a custom element */
11503     if (setup.team_mode &&
11504         tape.recording &&
11505         tape_action[i] &&
11506         !tape.player_participates[i])
11507       tape.player_participates[i] = TRUE;
11508   }
11509
11510   SetTapeActionFromMouseAction(tape_action,
11511                                &local_player->effective_mouse_action);
11512
11513   /* only record actions from input devices, but not programmed actions */
11514   if (tape.recording)
11515     TapeRecordAction(tape_action);
11516
11517 #if USE_NEW_PLAYER_ASSIGNMENTS
11518   // !!! also map player actions in single player mode !!!
11519   // if (game.team_mode)
11520   if (1)
11521   {
11522     byte mapped_action[MAX_PLAYERS];
11523
11524 #if DEBUG_PLAYER_ACTIONS
11525     printf(":::");
11526     for (i = 0; i < MAX_PLAYERS; i++)
11527       printf(" %d, ", stored_player[i].effective_action);
11528 #endif
11529
11530     for (i = 0; i < MAX_PLAYERS; i++)
11531       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11532
11533     for (i = 0; i < MAX_PLAYERS; i++)
11534       stored_player[i].effective_action = mapped_action[i];
11535
11536 #if DEBUG_PLAYER_ACTIONS
11537     printf(" =>");
11538     for (i = 0; i < MAX_PLAYERS; i++)
11539       printf(" %d, ", stored_player[i].effective_action);
11540     printf("\n");
11541 #endif
11542   }
11543 #if DEBUG_PLAYER_ACTIONS
11544   else
11545   {
11546     printf(":::");
11547     for (i = 0; i < MAX_PLAYERS; i++)
11548       printf(" %d, ", stored_player[i].effective_action);
11549     printf("\n");
11550   }
11551 #endif
11552 #endif
11553
11554   for (i = 0; i < MAX_PLAYERS; i++)
11555   {
11556     // allow engine snapshot in case of changed movement attempt
11557     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11558         (stored_player[i].effective_action & KEY_MOTION))
11559       game.snapshot.changed_action = TRUE;
11560
11561     // allow engine snapshot in case of snapping/dropping attempt
11562     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11563         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11564       game.snapshot.changed_action = TRUE;
11565
11566     game.snapshot.last_action[i] = stored_player[i].effective_action;
11567   }
11568
11569   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11570   {
11571     GameActions_EM_Main();
11572   }
11573   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11574   {
11575     GameActions_SP_Main();
11576   }
11577   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11578   {
11579     GameActions_MM_Main();
11580   }
11581   else
11582   {
11583     GameActions_RND_Main();
11584   }
11585
11586   BlitScreenToBitmap(backbuffer);
11587
11588   CheckLevelTime();
11589
11590   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11591
11592   if (global.show_frames_per_second)
11593   {
11594     static unsigned int fps_counter = 0;
11595     static int fps_frames = 0;
11596     unsigned int fps_delay_ms = Counter() - fps_counter;
11597
11598     fps_frames++;
11599
11600     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11601     {
11602       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11603
11604       fps_frames = 0;
11605       fps_counter = Counter();
11606
11607       /* always draw FPS to screen after FPS value was updated */
11608       redraw_mask |= REDRAW_FPS;
11609     }
11610
11611     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11612     if (GetDrawDeactivationMask() == REDRAW_NONE)
11613       redraw_mask |= REDRAW_FPS;
11614   }
11615 }
11616
11617 static void GameActions_CheckSaveEngineSnapshot()
11618 {
11619   if (!game.snapshot.save_snapshot)
11620     return;
11621
11622   // clear flag for saving snapshot _before_ saving snapshot
11623   game.snapshot.save_snapshot = FALSE;
11624
11625   SaveEngineSnapshotToList();
11626 }
11627
11628 void GameActions()
11629 {
11630   GameActionsExt();
11631
11632   GameActions_CheckSaveEngineSnapshot();
11633 }
11634
11635 void GameActions_EM_Main()
11636 {
11637   byte effective_action[MAX_PLAYERS];
11638   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11639   int i;
11640
11641   for (i = 0; i < MAX_PLAYERS; i++)
11642     effective_action[i] = stored_player[i].effective_action;
11643
11644   GameActions_EM(effective_action, warp_mode);
11645 }
11646
11647 void GameActions_SP_Main()
11648 {
11649   byte effective_action[MAX_PLAYERS];
11650   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11651   int i;
11652
11653   for (i = 0; i < MAX_PLAYERS; i++)
11654     effective_action[i] = stored_player[i].effective_action;
11655
11656   GameActions_SP(effective_action, warp_mode);
11657
11658   for (i = 0; i < MAX_PLAYERS; i++)
11659   {
11660     if (stored_player[i].force_dropping)
11661       stored_player[i].action |= KEY_BUTTON_DROP;
11662
11663     stored_player[i].force_dropping = FALSE;
11664   }
11665 }
11666
11667 void GameActions_MM_Main()
11668 {
11669   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11670
11671   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11672 }
11673
11674 void GameActions_RND_Main()
11675 {
11676   GameActions_RND();
11677 }
11678
11679 void GameActions_RND()
11680 {
11681   int magic_wall_x = 0, magic_wall_y = 0;
11682   int i, x, y, element, graphic, last_gfx_frame;
11683
11684   InitPlayfieldScanModeVars();
11685
11686   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11687   {
11688     SCAN_PLAYFIELD(x, y)
11689     {
11690       ChangeCount[x][y] = 0;
11691       ChangeEvent[x][y] = -1;
11692     }
11693   }
11694
11695   if (game.set_centered_player)
11696   {
11697     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11698
11699     /* switching to "all players" only possible if all players fit to screen */
11700     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11701     {
11702       game.centered_player_nr_next = game.centered_player_nr;
11703       game.set_centered_player = FALSE;
11704     }
11705
11706     /* do not switch focus to non-existing (or non-active) player */
11707     if (game.centered_player_nr_next >= 0 &&
11708         !stored_player[game.centered_player_nr_next].active)
11709     {
11710       game.centered_player_nr_next = game.centered_player_nr;
11711       game.set_centered_player = FALSE;
11712     }
11713   }
11714
11715   if (game.set_centered_player &&
11716       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11717   {
11718     int sx, sy;
11719
11720     if (game.centered_player_nr_next == -1)
11721     {
11722       setScreenCenteredToAllPlayers(&sx, &sy);
11723     }
11724     else
11725     {
11726       sx = stored_player[game.centered_player_nr_next].jx;
11727       sy = stored_player[game.centered_player_nr_next].jy;
11728     }
11729
11730     game.centered_player_nr = game.centered_player_nr_next;
11731     game.set_centered_player = FALSE;
11732
11733     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11734     DrawGameDoorValues();
11735   }
11736
11737   for (i = 0; i < MAX_PLAYERS; i++)
11738   {
11739     int actual_player_action = stored_player[i].effective_action;
11740
11741 #if 1
11742     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11743        - rnd_equinox_tetrachloride 048
11744        - rnd_equinox_tetrachloride_ii 096
11745        - rnd_emanuel_schmieg 002
11746        - doctor_sloan_ww 001, 020
11747     */
11748     if (stored_player[i].MovPos == 0)
11749       CheckGravityMovement(&stored_player[i]);
11750 #endif
11751
11752     /* overwrite programmed action with tape action */
11753     if (stored_player[i].programmed_action)
11754       actual_player_action = stored_player[i].programmed_action;
11755
11756     PlayerActions(&stored_player[i], actual_player_action);
11757
11758     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11759   }
11760
11761   ScrollScreen(NULL, SCROLL_GO_ON);
11762
11763   /* for backwards compatibility, the following code emulates a fixed bug that
11764      occured when pushing elements (causing elements that just made their last
11765      pushing step to already (if possible) make their first falling step in the
11766      same game frame, which is bad); this code is also needed to use the famous
11767      "spring push bug" which is used in older levels and might be wanted to be
11768      used also in newer levels, but in this case the buggy pushing code is only
11769      affecting the "spring" element and no other elements */
11770
11771   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11772   {
11773     for (i = 0; i < MAX_PLAYERS; i++)
11774     {
11775       struct PlayerInfo *player = &stored_player[i];
11776       int x = player->jx;
11777       int y = player->jy;
11778
11779       if (player->active && player->is_pushing && player->is_moving &&
11780           IS_MOVING(x, y) &&
11781           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11782            Feld[x][y] == EL_SPRING))
11783       {
11784         ContinueMoving(x, y);
11785
11786         /* continue moving after pushing (this is actually a bug) */
11787         if (!IS_MOVING(x, y))
11788           Stop[x][y] = FALSE;
11789       }
11790     }
11791   }
11792
11793   SCAN_PLAYFIELD(x, y)
11794   {
11795     ChangeCount[x][y] = 0;
11796     ChangeEvent[x][y] = -1;
11797
11798     /* this must be handled before main playfield loop */
11799     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11800     {
11801       MovDelay[x][y]--;
11802       if (MovDelay[x][y] <= 0)
11803         RemoveField(x, y);
11804     }
11805
11806     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11807     {
11808       MovDelay[x][y]--;
11809       if (MovDelay[x][y] <= 0)
11810       {
11811         RemoveField(x, y);
11812         TEST_DrawLevelField(x, y);
11813
11814         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11815       }
11816     }
11817
11818 #if DEBUG
11819     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11820     {
11821       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11822       printf("GameActions(): This should never happen!\n");
11823
11824       ChangePage[x][y] = -1;
11825     }
11826 #endif
11827
11828     Stop[x][y] = FALSE;
11829     if (WasJustMoving[x][y] > 0)
11830       WasJustMoving[x][y]--;
11831     if (WasJustFalling[x][y] > 0)
11832       WasJustFalling[x][y]--;
11833     if (CheckCollision[x][y] > 0)
11834       CheckCollision[x][y]--;
11835     if (CheckImpact[x][y] > 0)
11836       CheckImpact[x][y]--;
11837
11838     GfxFrame[x][y]++;
11839
11840     /* reset finished pushing action (not done in ContinueMoving() to allow
11841        continuous pushing animation for elements with zero push delay) */
11842     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11843     {
11844       ResetGfxAnimation(x, y);
11845       TEST_DrawLevelField(x, y);
11846     }
11847
11848 #if DEBUG
11849     if (IS_BLOCKED(x, y))
11850     {
11851       int oldx, oldy;
11852
11853       Blocked2Moving(x, y, &oldx, &oldy);
11854       if (!IS_MOVING(oldx, oldy))
11855       {
11856         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11857         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11858         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11859         printf("GameActions(): This should never happen!\n");
11860       }
11861     }
11862 #endif
11863   }
11864
11865   SCAN_PLAYFIELD(x, y)
11866   {
11867     element = Feld[x][y];
11868     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11869     last_gfx_frame = GfxFrame[x][y];
11870
11871     ResetGfxFrame(x, y);
11872
11873     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11874       DrawLevelGraphicAnimation(x, y, graphic);
11875
11876     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11877         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11878       ResetRandomAnimationValue(x, y);
11879
11880     SetRandomAnimationValue(x, y);
11881
11882     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11883
11884     if (IS_INACTIVE(element))
11885     {
11886       if (IS_ANIMATED(graphic))
11887         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11888
11889       continue;
11890     }
11891
11892     /* this may take place after moving, so 'element' may have changed */
11893     if (IS_CHANGING(x, y) &&
11894         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11895     {
11896       int page = element_info[element].event_page_nr[CE_DELAY];
11897
11898       HandleElementChange(x, y, page);
11899
11900       element = Feld[x][y];
11901       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11902     }
11903
11904     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11905     {
11906       StartMoving(x, y);
11907
11908       element = Feld[x][y];
11909       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11910
11911       if (IS_ANIMATED(graphic) &&
11912           !IS_MOVING(x, y) &&
11913           !Stop[x][y])
11914         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11915
11916       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11917         TEST_DrawTwinkleOnField(x, y);
11918     }
11919     else if (element == EL_ACID)
11920     {
11921       if (!Stop[x][y])
11922         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11923     }
11924     else if ((element == EL_EXIT_OPEN ||
11925               element == EL_EM_EXIT_OPEN ||
11926               element == EL_SP_EXIT_OPEN ||
11927               element == EL_STEEL_EXIT_OPEN ||
11928               element == EL_EM_STEEL_EXIT_OPEN ||
11929               element == EL_SP_TERMINAL ||
11930               element == EL_SP_TERMINAL_ACTIVE ||
11931               element == EL_EXTRA_TIME ||
11932               element == EL_SHIELD_NORMAL ||
11933               element == EL_SHIELD_DEADLY) &&
11934              IS_ANIMATED(graphic))
11935       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11936     else if (IS_MOVING(x, y))
11937       ContinueMoving(x, y);
11938     else if (IS_ACTIVE_BOMB(element))
11939       CheckDynamite(x, y);
11940     else if (element == EL_AMOEBA_GROWING)
11941       AmoebeWaechst(x, y);
11942     else if (element == EL_AMOEBA_SHRINKING)
11943       AmoebaDisappearing(x, y);
11944
11945 #if !USE_NEW_AMOEBA_CODE
11946     else if (IS_AMOEBALIVE(element))
11947       AmoebeAbleger(x, y);
11948 #endif
11949
11950     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11951       Life(x, y);
11952     else if (element == EL_EXIT_CLOSED)
11953       CheckExit(x, y);
11954     else if (element == EL_EM_EXIT_CLOSED)
11955       CheckExitEM(x, y);
11956     else if (element == EL_STEEL_EXIT_CLOSED)
11957       CheckExitSteel(x, y);
11958     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11959       CheckExitSteelEM(x, y);
11960     else if (element == EL_SP_EXIT_CLOSED)
11961       CheckExitSP(x, y);
11962     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11963              element == EL_EXPANDABLE_STEELWALL_GROWING)
11964       MauerWaechst(x, y);
11965     else if (element == EL_EXPANDABLE_WALL ||
11966              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11967              element == EL_EXPANDABLE_WALL_VERTICAL ||
11968              element == EL_EXPANDABLE_WALL_ANY ||
11969              element == EL_BD_EXPANDABLE_WALL)
11970       MauerAbleger(x, y);
11971     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11972              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11973              element == EL_EXPANDABLE_STEELWALL_ANY)
11974       MauerAblegerStahl(x, y);
11975     else if (element == EL_FLAMES)
11976       CheckForDragon(x, y);
11977     else if (element == EL_EXPLOSION)
11978       ; /* drawing of correct explosion animation is handled separately */
11979     else if (element == EL_ELEMENT_SNAPPING ||
11980              element == EL_DIAGONAL_SHRINKING ||
11981              element == EL_DIAGONAL_GROWING)
11982     {
11983       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11984
11985       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11986     }
11987     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11988       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11989
11990     if (IS_BELT_ACTIVE(element))
11991       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11992
11993     if (game.magic_wall_active)
11994     {
11995       int jx = local_player->jx, jy = local_player->jy;
11996
11997       /* play the element sound at the position nearest to the player */
11998       if ((element == EL_MAGIC_WALL_FULL ||
11999            element == EL_MAGIC_WALL_ACTIVE ||
12000            element == EL_MAGIC_WALL_EMPTYING ||
12001            element == EL_BD_MAGIC_WALL_FULL ||
12002            element == EL_BD_MAGIC_WALL_ACTIVE ||
12003            element == EL_BD_MAGIC_WALL_EMPTYING ||
12004            element == EL_DC_MAGIC_WALL_FULL ||
12005            element == EL_DC_MAGIC_WALL_ACTIVE ||
12006            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12007           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12008       {
12009         magic_wall_x = x;
12010         magic_wall_y = y;
12011       }
12012     }
12013   }
12014
12015 #if USE_NEW_AMOEBA_CODE
12016   /* new experimental amoeba growth stuff */
12017   if (!(FrameCounter % 8))
12018   {
12019     static unsigned int random = 1684108901;
12020
12021     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12022     {
12023       x = RND(lev_fieldx);
12024       y = RND(lev_fieldy);
12025       element = Feld[x][y];
12026
12027       if (!IS_PLAYER(x,y) &&
12028           (element == EL_EMPTY ||
12029            CAN_GROW_INTO(element) ||
12030            element == EL_QUICKSAND_EMPTY ||
12031            element == EL_QUICKSAND_FAST_EMPTY ||
12032            element == EL_ACID_SPLASH_LEFT ||
12033            element == EL_ACID_SPLASH_RIGHT))
12034       {
12035         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12036             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12037             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12038             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12039           Feld[x][y] = EL_AMOEBA_DROP;
12040       }
12041
12042       random = random * 129 + 1;
12043     }
12044   }
12045 #endif
12046
12047   game.explosions_delayed = FALSE;
12048
12049   SCAN_PLAYFIELD(x, y)
12050   {
12051     element = Feld[x][y];
12052
12053     if (ExplodeField[x][y])
12054       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12055     else if (element == EL_EXPLOSION)
12056       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12057
12058     ExplodeField[x][y] = EX_TYPE_NONE;
12059   }
12060
12061   game.explosions_delayed = TRUE;
12062
12063   if (game.magic_wall_active)
12064   {
12065     if (!(game.magic_wall_time_left % 4))
12066     {
12067       int element = Feld[magic_wall_x][magic_wall_y];
12068
12069       if (element == EL_BD_MAGIC_WALL_FULL ||
12070           element == EL_BD_MAGIC_WALL_ACTIVE ||
12071           element == EL_BD_MAGIC_WALL_EMPTYING)
12072         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12073       else if (element == EL_DC_MAGIC_WALL_FULL ||
12074                element == EL_DC_MAGIC_WALL_ACTIVE ||
12075                element == EL_DC_MAGIC_WALL_EMPTYING)
12076         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12077       else
12078         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12079     }
12080
12081     if (game.magic_wall_time_left > 0)
12082     {
12083       game.magic_wall_time_left--;
12084
12085       if (!game.magic_wall_time_left)
12086       {
12087         SCAN_PLAYFIELD(x, y)
12088         {
12089           element = Feld[x][y];
12090
12091           if (element == EL_MAGIC_WALL_ACTIVE ||
12092               element == EL_MAGIC_WALL_FULL)
12093           {
12094             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12095             TEST_DrawLevelField(x, y);
12096           }
12097           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12098                    element == EL_BD_MAGIC_WALL_FULL)
12099           {
12100             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12101             TEST_DrawLevelField(x, y);
12102           }
12103           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12104                    element == EL_DC_MAGIC_WALL_FULL)
12105           {
12106             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12107             TEST_DrawLevelField(x, y);
12108           }
12109         }
12110
12111         game.magic_wall_active = FALSE;
12112       }
12113     }
12114   }
12115
12116   if (game.light_time_left > 0)
12117   {
12118     game.light_time_left--;
12119
12120     if (game.light_time_left == 0)
12121       RedrawAllLightSwitchesAndInvisibleElements();
12122   }
12123
12124   if (game.timegate_time_left > 0)
12125   {
12126     game.timegate_time_left--;
12127
12128     if (game.timegate_time_left == 0)
12129       CloseAllOpenTimegates();
12130   }
12131
12132   if (game.lenses_time_left > 0)
12133   {
12134     game.lenses_time_left--;
12135
12136     if (game.lenses_time_left == 0)
12137       RedrawAllInvisibleElementsForLenses();
12138   }
12139
12140   if (game.magnify_time_left > 0)
12141   {
12142     game.magnify_time_left--;
12143
12144     if (game.magnify_time_left == 0)
12145       RedrawAllInvisibleElementsForMagnifier();
12146   }
12147
12148   for (i = 0; i < MAX_PLAYERS; i++)
12149   {
12150     struct PlayerInfo *player = &stored_player[i];
12151
12152     if (SHIELD_ON(player))
12153     {
12154       if (player->shield_deadly_time_left)
12155         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12156       else if (player->shield_normal_time_left)
12157         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12158     }
12159   }
12160
12161 #if USE_DELAYED_GFX_REDRAW
12162   SCAN_PLAYFIELD(x, y)
12163   {
12164     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12165     {
12166       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12167          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12168
12169       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12170         DrawLevelField(x, y);
12171
12172       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12173         DrawLevelFieldCrumbled(x, y);
12174
12175       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12176         DrawLevelFieldCrumbledNeighbours(x, y);
12177
12178       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12179         DrawTwinkleOnField(x, y);
12180     }
12181
12182     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12183   }
12184 #endif
12185
12186   DrawAllPlayers();
12187   PlayAllPlayersSound();
12188
12189   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12190   {
12191     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12192
12193     local_player->show_envelope = 0;
12194   }
12195
12196   /* use random number generator in every frame to make it less predictable */
12197   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12198     RND(1);
12199 }
12200
12201 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12202 {
12203   int min_x = x, min_y = y, max_x = x, max_y = y;
12204   int i;
12205
12206   for (i = 0; i < MAX_PLAYERS; i++)
12207   {
12208     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12209
12210     if (!stored_player[i].active || &stored_player[i] == player)
12211       continue;
12212
12213     min_x = MIN(min_x, jx);
12214     min_y = MIN(min_y, jy);
12215     max_x = MAX(max_x, jx);
12216     max_y = MAX(max_y, jy);
12217   }
12218
12219   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12220 }
12221
12222 static boolean AllPlayersInVisibleScreen()
12223 {
12224   int i;
12225
12226   for (i = 0; i < MAX_PLAYERS; i++)
12227   {
12228     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12229
12230     if (!stored_player[i].active)
12231       continue;
12232
12233     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12234       return FALSE;
12235   }
12236
12237   return TRUE;
12238 }
12239
12240 void ScrollLevel(int dx, int dy)
12241 {
12242   int scroll_offset = 2 * TILEX_VAR;
12243   int x, y;
12244
12245   BlitBitmap(drawto_field, drawto_field,
12246              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12247              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12248              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12249              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12250              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12251              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12252
12253   if (dx != 0)
12254   {
12255     x = (dx == 1 ? BX1 : BX2);
12256     for (y = BY1; y <= BY2; y++)
12257       DrawScreenField(x, y);
12258   }
12259
12260   if (dy != 0)
12261   {
12262     y = (dy == 1 ? BY1 : BY2);
12263     for (x = BX1; x <= BX2; x++)
12264       DrawScreenField(x, y);
12265   }
12266
12267   redraw_mask |= REDRAW_FIELD;
12268 }
12269
12270 static boolean canFallDown(struct PlayerInfo *player)
12271 {
12272   int jx = player->jx, jy = player->jy;
12273
12274   return (IN_LEV_FIELD(jx, jy + 1) &&
12275           (IS_FREE(jx, jy + 1) ||
12276            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12277           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12278           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12279 }
12280
12281 static boolean canPassField(int x, int y, int move_dir)
12282 {
12283   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12284   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12285   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12286   int nextx = x + dx;
12287   int nexty = y + dy;
12288   int element = Feld[x][y];
12289
12290   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12291           !CAN_MOVE(element) &&
12292           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12293           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12294           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12295 }
12296
12297 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12298 {
12299   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12300   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12301   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12302   int newx = x + dx;
12303   int newy = y + dy;
12304
12305   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12306           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12307           (IS_DIGGABLE(Feld[newx][newy]) ||
12308            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12309            canPassField(newx, newy, move_dir)));
12310 }
12311
12312 static void CheckGravityMovement(struct PlayerInfo *player)
12313 {
12314   if (player->gravity && !player->programmed_action)
12315   {
12316     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12317     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12318     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12319     int jx = player->jx, jy = player->jy;
12320     boolean player_is_moving_to_valid_field =
12321       (!player_is_snapping &&
12322        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12323         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12324     boolean player_can_fall_down = canFallDown(player);
12325
12326     if (player_can_fall_down &&
12327         !player_is_moving_to_valid_field)
12328       player->programmed_action = MV_DOWN;
12329   }
12330 }
12331
12332 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12333 {
12334   return CheckGravityMovement(player);
12335
12336   if (player->gravity && !player->programmed_action)
12337   {
12338     int jx = player->jx, jy = player->jy;
12339     boolean field_under_player_is_free =
12340       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12341     boolean player_is_standing_on_valid_field =
12342       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12343        (IS_WALKABLE(Feld[jx][jy]) &&
12344         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12345
12346     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12347       player->programmed_action = MV_DOWN;
12348   }
12349 }
12350
12351 /*
12352   MovePlayerOneStep()
12353   -----------------------------------------------------------------------------
12354   dx, dy:               direction (non-diagonal) to try to move the player to
12355   real_dx, real_dy:     direction as read from input device (can be diagonal)
12356 */
12357
12358 boolean MovePlayerOneStep(struct PlayerInfo *player,
12359                           int dx, int dy, int real_dx, int real_dy)
12360 {
12361   int jx = player->jx, jy = player->jy;
12362   int new_jx = jx + dx, new_jy = jy + dy;
12363   int can_move;
12364   boolean player_can_move = !player->cannot_move;
12365
12366   if (!player->active || (!dx && !dy))
12367     return MP_NO_ACTION;
12368
12369   player->MovDir = (dx < 0 ? MV_LEFT :
12370                     dx > 0 ? MV_RIGHT :
12371                     dy < 0 ? MV_UP :
12372                     dy > 0 ? MV_DOWN :  MV_NONE);
12373
12374   if (!IN_LEV_FIELD(new_jx, new_jy))
12375     return MP_NO_ACTION;
12376
12377   if (!player_can_move)
12378   {
12379     if (player->MovPos == 0)
12380     {
12381       player->is_moving = FALSE;
12382       player->is_digging = FALSE;
12383       player->is_collecting = FALSE;
12384       player->is_snapping = FALSE;
12385       player->is_pushing = FALSE;
12386     }
12387   }
12388
12389   if (!network.enabled && game.centered_player_nr == -1 &&
12390       !AllPlayersInSight(player, new_jx, new_jy))
12391     return MP_NO_ACTION;
12392
12393   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12394   if (can_move != MP_MOVING)
12395     return can_move;
12396
12397   /* check if DigField() has caused relocation of the player */
12398   if (player->jx != jx || player->jy != jy)
12399     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12400
12401   StorePlayer[jx][jy] = 0;
12402   player->last_jx = jx;
12403   player->last_jy = jy;
12404   player->jx = new_jx;
12405   player->jy = new_jy;
12406   StorePlayer[new_jx][new_jy] = player->element_nr;
12407
12408   if (player->move_delay_value_next != -1)
12409   {
12410     player->move_delay_value = player->move_delay_value_next;
12411     player->move_delay_value_next = -1;
12412   }
12413
12414   player->MovPos =
12415     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12416
12417   player->step_counter++;
12418
12419   PlayerVisit[jx][jy] = FrameCounter;
12420
12421   player->is_moving = TRUE;
12422
12423 #if 1
12424   /* should better be called in MovePlayer(), but this breaks some tapes */
12425   ScrollPlayer(player, SCROLL_INIT);
12426 #endif
12427
12428   return MP_MOVING;
12429 }
12430
12431 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12432 {
12433   int jx = player->jx, jy = player->jy;
12434   int old_jx = jx, old_jy = jy;
12435   int moved = MP_NO_ACTION;
12436
12437   if (!player->active)
12438     return FALSE;
12439
12440   if (!dx && !dy)
12441   {
12442     if (player->MovPos == 0)
12443     {
12444       player->is_moving = FALSE;
12445       player->is_digging = FALSE;
12446       player->is_collecting = FALSE;
12447       player->is_snapping = FALSE;
12448       player->is_pushing = FALSE;
12449     }
12450
12451     return FALSE;
12452   }
12453
12454   if (player->move_delay > 0)
12455     return FALSE;
12456
12457   player->move_delay = -1;              /* set to "uninitialized" value */
12458
12459   /* store if player is automatically moved to next field */
12460   player->is_auto_moving = (player->programmed_action != MV_NONE);
12461
12462   /* remove the last programmed player action */
12463   player->programmed_action = 0;
12464
12465   if (player->MovPos)
12466   {
12467     /* should only happen if pre-1.2 tape recordings are played */
12468     /* this is only for backward compatibility */
12469
12470     int original_move_delay_value = player->move_delay_value;
12471
12472 #if DEBUG
12473     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12474            tape.counter);
12475 #endif
12476
12477     /* scroll remaining steps with finest movement resolution */
12478     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12479
12480     while (player->MovPos)
12481     {
12482       ScrollPlayer(player, SCROLL_GO_ON);
12483       ScrollScreen(NULL, SCROLL_GO_ON);
12484
12485       AdvanceFrameAndPlayerCounters(player->index_nr);
12486
12487       DrawAllPlayers();
12488       BackToFront_WithFrameDelay(0);
12489     }
12490
12491     player->move_delay_value = original_move_delay_value;
12492   }
12493
12494   player->is_active = FALSE;
12495
12496   if (player->last_move_dir & MV_HORIZONTAL)
12497   {
12498     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12499       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12500   }
12501   else
12502   {
12503     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12504       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12505   }
12506
12507   if (!moved && !player->is_active)
12508   {
12509     player->is_moving = FALSE;
12510     player->is_digging = FALSE;
12511     player->is_collecting = FALSE;
12512     player->is_snapping = FALSE;
12513     player->is_pushing = FALSE;
12514   }
12515
12516   jx = player->jx;
12517   jy = player->jy;
12518
12519   if (moved & MP_MOVING && !ScreenMovPos &&
12520       (player->index_nr == game.centered_player_nr ||
12521        game.centered_player_nr == -1))
12522   {
12523     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12524     int offset = game.scroll_delay_value;
12525
12526     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12527     {
12528       /* actual player has left the screen -- scroll in that direction */
12529       if (jx != old_jx)         /* player has moved horizontally */
12530         scroll_x += (jx - old_jx);
12531       else                      /* player has moved vertically */
12532         scroll_y += (jy - old_jy);
12533     }
12534     else
12535     {
12536       if (jx != old_jx)         /* player has moved horizontally */
12537       {
12538         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12539             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12540           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12541
12542         /* don't scroll over playfield boundaries */
12543         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12544           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12545
12546         /* don't scroll more than one field at a time */
12547         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12548
12549         /* don't scroll against the player's moving direction */
12550         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12551             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12552           scroll_x = old_scroll_x;
12553       }
12554       else                      /* player has moved vertically */
12555       {
12556         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12557             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12558           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12559
12560         /* don't scroll over playfield boundaries */
12561         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12562           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12563
12564         /* don't scroll more than one field at a time */
12565         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12566
12567         /* don't scroll against the player's moving direction */
12568         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12569             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12570           scroll_y = old_scroll_y;
12571       }
12572     }
12573
12574     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12575     {
12576       if (!network.enabled && game.centered_player_nr == -1 &&
12577           !AllPlayersInVisibleScreen())
12578       {
12579         scroll_x = old_scroll_x;
12580         scroll_y = old_scroll_y;
12581       }
12582       else
12583       {
12584         ScrollScreen(player, SCROLL_INIT);
12585         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12586       }
12587     }
12588   }
12589
12590   player->StepFrame = 0;
12591
12592   if (moved & MP_MOVING)
12593   {
12594     if (old_jx != jx && old_jy == jy)
12595       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12596     else if (old_jx == jx && old_jy != jy)
12597       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12598
12599     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12600
12601     player->last_move_dir = player->MovDir;
12602     player->is_moving = TRUE;
12603     player->is_snapping = FALSE;
12604     player->is_switching = FALSE;
12605     player->is_dropping = FALSE;
12606     player->is_dropping_pressed = FALSE;
12607     player->drop_pressed_delay = 0;
12608
12609 #if 0
12610     /* should better be called here than above, but this breaks some tapes */
12611     ScrollPlayer(player, SCROLL_INIT);
12612 #endif
12613   }
12614   else
12615   {
12616     CheckGravityMovementWhenNotMoving(player);
12617
12618     player->is_moving = FALSE;
12619
12620     /* at this point, the player is allowed to move, but cannot move right now
12621        (e.g. because of something blocking the way) -- ensure that the player
12622        is also allowed to move in the next frame (in old versions before 3.1.1,
12623        the player was forced to wait again for eight frames before next try) */
12624
12625     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12626       player->move_delay = 0;   /* allow direct movement in the next frame */
12627   }
12628
12629   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12630     player->move_delay = player->move_delay_value;
12631
12632   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12633   {
12634     TestIfPlayerTouchesBadThing(jx, jy);
12635     TestIfPlayerTouchesCustomElement(jx, jy);
12636   }
12637
12638   if (!player->active)
12639     RemovePlayer(player);
12640
12641   return moved;
12642 }
12643
12644 void ScrollPlayer(struct PlayerInfo *player, int mode)
12645 {
12646   int jx = player->jx, jy = player->jy;
12647   int last_jx = player->last_jx, last_jy = player->last_jy;
12648   int move_stepsize = TILEX / player->move_delay_value;
12649
12650   if (!player->active)
12651     return;
12652
12653   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12654     return;
12655
12656   if (mode == SCROLL_INIT)
12657   {
12658     player->actual_frame_counter = FrameCounter;
12659     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12660
12661     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12662         Feld[last_jx][last_jy] == EL_EMPTY)
12663     {
12664       int last_field_block_delay = 0;   /* start with no blocking at all */
12665       int block_delay_adjustment = player->block_delay_adjustment;
12666
12667       /* if player blocks last field, add delay for exactly one move */
12668       if (player->block_last_field)
12669       {
12670         last_field_block_delay += player->move_delay_value;
12671
12672         /* when blocking enabled, prevent moving up despite gravity */
12673         if (player->gravity && player->MovDir == MV_UP)
12674           block_delay_adjustment = -1;
12675       }
12676
12677       /* add block delay adjustment (also possible when not blocking) */
12678       last_field_block_delay += block_delay_adjustment;
12679
12680       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12681       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12682     }
12683
12684     if (player->MovPos != 0)    /* player has not yet reached destination */
12685       return;
12686   }
12687   else if (!FrameReached(&player->actual_frame_counter, 1))
12688     return;
12689
12690   if (player->MovPos != 0)
12691   {
12692     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12693     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12694
12695     /* before DrawPlayer() to draw correct player graphic for this case */
12696     if (player->MovPos == 0)
12697       CheckGravityMovement(player);
12698   }
12699
12700   if (player->MovPos == 0)      /* player reached destination field */
12701   {
12702     if (player->move_delay_reset_counter > 0)
12703     {
12704       player->move_delay_reset_counter--;
12705
12706       if (player->move_delay_reset_counter == 0)
12707       {
12708         /* continue with normal speed after quickly moving through gate */
12709         HALVE_PLAYER_SPEED(player);
12710
12711         /* be able to make the next move without delay */
12712         player->move_delay = 0;
12713       }
12714     }
12715
12716     player->last_jx = jx;
12717     player->last_jy = jy;
12718
12719     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12720         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12721         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12722         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12723         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12724         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12725         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12726         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12727     {
12728       DrawPlayer(player);       /* needed here only to cleanup last field */
12729       RemovePlayer(player);
12730
12731       if (local_player->friends_still_needed == 0 ||
12732           IS_SP_ELEMENT(Feld[jx][jy]))
12733         PlayerWins(player);
12734     }
12735
12736     /* this breaks one level: "machine", level 000 */
12737     {
12738       int move_direction = player->MovDir;
12739       int enter_side = MV_DIR_OPPOSITE(move_direction);
12740       int leave_side = move_direction;
12741       int old_jx = last_jx;
12742       int old_jy = last_jy;
12743       int old_element = Feld[old_jx][old_jy];
12744       int new_element = Feld[jx][jy];
12745
12746       if (IS_CUSTOM_ELEMENT(old_element))
12747         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12748                                    CE_LEFT_BY_PLAYER,
12749                                    player->index_bit, leave_side);
12750
12751       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12752                                           CE_PLAYER_LEAVES_X,
12753                                           player->index_bit, leave_side);
12754
12755       if (IS_CUSTOM_ELEMENT(new_element))
12756         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12757                                    player->index_bit, enter_side);
12758
12759       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12760                                           CE_PLAYER_ENTERS_X,
12761                                           player->index_bit, enter_side);
12762
12763       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12764                                         CE_MOVE_OF_X, move_direction);
12765     }
12766
12767     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12768     {
12769       TestIfPlayerTouchesBadThing(jx, jy);
12770       TestIfPlayerTouchesCustomElement(jx, jy);
12771
12772       /* needed because pushed element has not yet reached its destination,
12773          so it would trigger a change event at its previous field location */
12774       if (!player->is_pushing)
12775         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12776
12777       if (!player->active)
12778         RemovePlayer(player);
12779     }
12780
12781     if (!local_player->LevelSolved && level.use_step_counter)
12782     {
12783       int i;
12784
12785       TimePlayed++;
12786
12787       if (TimeLeft > 0)
12788       {
12789         TimeLeft--;
12790
12791         if (TimeLeft <= 10 && setup.time_limit)
12792           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12793
12794         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12795
12796         DisplayGameControlValues();
12797
12798         if (!TimeLeft && setup.time_limit)
12799           for (i = 0; i < MAX_PLAYERS; i++)
12800             KillPlayer(&stored_player[i]);
12801       }
12802       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12803       {
12804         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12805
12806         DisplayGameControlValues();
12807       }
12808     }
12809
12810     if (tape.single_step && tape.recording && !tape.pausing &&
12811         !player->programmed_action)
12812       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12813
12814     if (!player->programmed_action)
12815       CheckSaveEngineSnapshot(player);
12816   }
12817 }
12818
12819 void ScrollScreen(struct PlayerInfo *player, int mode)
12820 {
12821   static unsigned int screen_frame_counter = 0;
12822
12823   if (mode == SCROLL_INIT)
12824   {
12825     /* set scrolling step size according to actual player's moving speed */
12826     ScrollStepSize = TILEX / player->move_delay_value;
12827
12828     screen_frame_counter = FrameCounter;
12829     ScreenMovDir = player->MovDir;
12830     ScreenMovPos = player->MovPos;
12831     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12832     return;
12833   }
12834   else if (!FrameReached(&screen_frame_counter, 1))
12835     return;
12836
12837   if (ScreenMovPos)
12838   {
12839     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12840     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12841     redraw_mask |= REDRAW_FIELD;
12842   }
12843   else
12844     ScreenMovDir = MV_NONE;
12845 }
12846
12847 void TestIfPlayerTouchesCustomElement(int x, int y)
12848 {
12849   static int xy[4][2] =
12850   {
12851     { 0, -1 },
12852     { -1, 0 },
12853     { +1, 0 },
12854     { 0, +1 }
12855   };
12856   static int trigger_sides[4][2] =
12857   {
12858     /* center side       border side */
12859     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12860     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12861     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12862     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12863   };
12864   static int touch_dir[4] =
12865   {
12866     MV_LEFT | MV_RIGHT,
12867     MV_UP   | MV_DOWN,
12868     MV_UP   | MV_DOWN,
12869     MV_LEFT | MV_RIGHT
12870   };
12871   int center_element = Feld[x][y];      /* should always be non-moving! */
12872   int i;
12873
12874   for (i = 0; i < NUM_DIRECTIONS; i++)
12875   {
12876     int xx = x + xy[i][0];
12877     int yy = y + xy[i][1];
12878     int center_side = trigger_sides[i][0];
12879     int border_side = trigger_sides[i][1];
12880     int border_element;
12881
12882     if (!IN_LEV_FIELD(xx, yy))
12883       continue;
12884
12885     if (IS_PLAYER(x, y))                /* player found at center element */
12886     {
12887       struct PlayerInfo *player = PLAYERINFO(x, y);
12888
12889       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12890         border_element = Feld[xx][yy];          /* may be moving! */
12891       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12892         border_element = Feld[xx][yy];
12893       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12894         border_element = MovingOrBlocked2Element(xx, yy);
12895       else
12896         continue;               /* center and border element do not touch */
12897
12898       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12899                                  player->index_bit, border_side);
12900       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12901                                           CE_PLAYER_TOUCHES_X,
12902                                           player->index_bit, border_side);
12903
12904       {
12905         /* use player element that is initially defined in the level playfield,
12906            not the player element that corresponds to the runtime player number
12907            (example: a level that contains EL_PLAYER_3 as the only player would
12908            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12909         int player_element = PLAYERINFO(x, y)->initial_element;
12910
12911         CheckElementChangeBySide(xx, yy, border_element, player_element,
12912                                  CE_TOUCHING_X, border_side);
12913       }
12914     }
12915     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12916     {
12917       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12918
12919       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12920       {
12921         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12922           continue;             /* center and border element do not touch */
12923       }
12924
12925       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12926                                  player->index_bit, center_side);
12927       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12928                                           CE_PLAYER_TOUCHES_X,
12929                                           player->index_bit, center_side);
12930
12931       {
12932         /* use player element that is initially defined in the level playfield,
12933            not the player element that corresponds to the runtime player number
12934            (example: a level that contains EL_PLAYER_3 as the only player would
12935            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12936         int player_element = PLAYERINFO(xx, yy)->initial_element;
12937
12938         CheckElementChangeBySide(x, y, center_element, player_element,
12939                                  CE_TOUCHING_X, center_side);
12940       }
12941
12942       break;
12943     }
12944   }
12945 }
12946
12947 void TestIfElementTouchesCustomElement(int x, int y)
12948 {
12949   static int xy[4][2] =
12950   {
12951     { 0, -1 },
12952     { -1, 0 },
12953     { +1, 0 },
12954     { 0, +1 }
12955   };
12956   static int trigger_sides[4][2] =
12957   {
12958     /* center side      border side */
12959     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12960     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12961     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12962     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12963   };
12964   static int touch_dir[4] =
12965   {
12966     MV_LEFT | MV_RIGHT,
12967     MV_UP   | MV_DOWN,
12968     MV_UP   | MV_DOWN,
12969     MV_LEFT | MV_RIGHT
12970   };
12971   boolean change_center_element = FALSE;
12972   int center_element = Feld[x][y];      /* should always be non-moving! */
12973   int border_element_old[NUM_DIRECTIONS];
12974   int i;
12975
12976   for (i = 0; i < NUM_DIRECTIONS; i++)
12977   {
12978     int xx = x + xy[i][0];
12979     int yy = y + xy[i][1];
12980     int border_element;
12981
12982     border_element_old[i] = -1;
12983
12984     if (!IN_LEV_FIELD(xx, yy))
12985       continue;
12986
12987     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12988       border_element = Feld[xx][yy];    /* may be moving! */
12989     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12990       border_element = Feld[xx][yy];
12991     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12992       border_element = MovingOrBlocked2Element(xx, yy);
12993     else
12994       continue;                 /* center and border element do not touch */
12995
12996     border_element_old[i] = border_element;
12997   }
12998
12999   for (i = 0; i < NUM_DIRECTIONS; i++)
13000   {
13001     int xx = x + xy[i][0];
13002     int yy = y + xy[i][1];
13003     int center_side = trigger_sides[i][0];
13004     int border_element = border_element_old[i];
13005
13006     if (border_element == -1)
13007       continue;
13008
13009     /* check for change of border element */
13010     CheckElementChangeBySide(xx, yy, border_element, center_element,
13011                              CE_TOUCHING_X, center_side);
13012
13013     /* (center element cannot be player, so we dont have to check this here) */
13014   }
13015
13016   for (i = 0; i < NUM_DIRECTIONS; i++)
13017   {
13018     int xx = x + xy[i][0];
13019     int yy = y + xy[i][1];
13020     int border_side = trigger_sides[i][1];
13021     int border_element = border_element_old[i];
13022
13023     if (border_element == -1)
13024       continue;
13025
13026     /* check for change of center element (but change it only once) */
13027     if (!change_center_element)
13028       change_center_element =
13029         CheckElementChangeBySide(x, y, center_element, border_element,
13030                                  CE_TOUCHING_X, border_side);
13031
13032     if (IS_PLAYER(xx, yy))
13033     {
13034       /* use player element that is initially defined in the level playfield,
13035          not the player element that corresponds to the runtime player number
13036          (example: a level that contains EL_PLAYER_3 as the only player would
13037          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13038       int player_element = PLAYERINFO(xx, yy)->initial_element;
13039
13040       CheckElementChangeBySide(x, y, center_element, player_element,
13041                                CE_TOUCHING_X, border_side);
13042     }
13043   }
13044 }
13045
13046 void TestIfElementHitsCustomElement(int x, int y, int direction)
13047 {
13048   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13049   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13050   int hitx = x + dx, hity = y + dy;
13051   int hitting_element = Feld[x][y];
13052   int touched_element;
13053
13054   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13055     return;
13056
13057   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13058                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13059
13060   if (IN_LEV_FIELD(hitx, hity))
13061   {
13062     int opposite_direction = MV_DIR_OPPOSITE(direction);
13063     int hitting_side = direction;
13064     int touched_side = opposite_direction;
13065     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13066                           MovDir[hitx][hity] != direction ||
13067                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13068
13069     object_hit = TRUE;
13070
13071     if (object_hit)
13072     {
13073       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13074                                CE_HITTING_X, touched_side);
13075
13076       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13077                                CE_HIT_BY_X, hitting_side);
13078
13079       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13080                                CE_HIT_BY_SOMETHING, opposite_direction);
13081
13082       if (IS_PLAYER(hitx, hity))
13083       {
13084         /* use player element that is initially defined in the level playfield,
13085            not the player element that corresponds to the runtime player number
13086            (example: a level that contains EL_PLAYER_3 as the only player would
13087            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13088         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13089
13090         CheckElementChangeBySide(x, y, hitting_element, player_element,
13091                                  CE_HITTING_X, touched_side);
13092       }
13093     }
13094   }
13095
13096   /* "hitting something" is also true when hitting the playfield border */
13097   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13098                            CE_HITTING_SOMETHING, direction);
13099 }
13100
13101 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13102 {
13103   int i, kill_x = -1, kill_y = -1;
13104
13105   int bad_element = -1;
13106   static int test_xy[4][2] =
13107   {
13108     { 0, -1 },
13109     { -1, 0 },
13110     { +1, 0 },
13111     { 0, +1 }
13112   };
13113   static int test_dir[4] =
13114   {
13115     MV_UP,
13116     MV_LEFT,
13117     MV_RIGHT,
13118     MV_DOWN
13119   };
13120
13121   for (i = 0; i < NUM_DIRECTIONS; i++)
13122   {
13123     int test_x, test_y, test_move_dir, test_element;
13124
13125     test_x = good_x + test_xy[i][0];
13126     test_y = good_y + test_xy[i][1];
13127
13128     if (!IN_LEV_FIELD(test_x, test_y))
13129       continue;
13130
13131     test_move_dir =
13132       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13133
13134     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13135
13136     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13137        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13138     */
13139     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13140         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13141     {
13142       kill_x = test_x;
13143       kill_y = test_y;
13144       bad_element = test_element;
13145
13146       break;
13147     }
13148   }
13149
13150   if (kill_x != -1 || kill_y != -1)
13151   {
13152     if (IS_PLAYER(good_x, good_y))
13153     {
13154       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13155
13156       if (player->shield_deadly_time_left > 0 &&
13157           !IS_INDESTRUCTIBLE(bad_element))
13158         Bang(kill_x, kill_y);
13159       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13160         KillPlayer(player);
13161     }
13162     else
13163       Bang(good_x, good_y);
13164   }
13165 }
13166
13167 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13168 {
13169   int i, kill_x = -1, kill_y = -1;
13170   int bad_element = Feld[bad_x][bad_y];
13171   static int test_xy[4][2] =
13172   {
13173     { 0, -1 },
13174     { -1, 0 },
13175     { +1, 0 },
13176     { 0, +1 }
13177   };
13178   static int touch_dir[4] =
13179   {
13180     MV_LEFT | MV_RIGHT,
13181     MV_UP   | MV_DOWN,
13182     MV_UP   | MV_DOWN,
13183     MV_LEFT | MV_RIGHT
13184   };
13185   static int test_dir[4] =
13186   {
13187     MV_UP,
13188     MV_LEFT,
13189     MV_RIGHT,
13190     MV_DOWN
13191   };
13192
13193   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13194     return;
13195
13196   for (i = 0; i < NUM_DIRECTIONS; i++)
13197   {
13198     int test_x, test_y, test_move_dir, test_element;
13199
13200     test_x = bad_x + test_xy[i][0];
13201     test_y = bad_y + test_xy[i][1];
13202
13203     if (!IN_LEV_FIELD(test_x, test_y))
13204       continue;
13205
13206     test_move_dir =
13207       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13208
13209     test_element = Feld[test_x][test_y];
13210
13211     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13212        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13213     */
13214     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13215         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13216     {
13217       /* good thing is player or penguin that does not move away */
13218       if (IS_PLAYER(test_x, test_y))
13219       {
13220         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13221
13222         if (bad_element == EL_ROBOT && player->is_moving)
13223           continue;     /* robot does not kill player if he is moving */
13224
13225         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13226         {
13227           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13228             continue;           /* center and border element do not touch */
13229         }
13230
13231         kill_x = test_x;
13232         kill_y = test_y;
13233
13234         break;
13235       }
13236       else if (test_element == EL_PENGUIN)
13237       {
13238         kill_x = test_x;
13239         kill_y = test_y;
13240
13241         break;
13242       }
13243     }
13244   }
13245
13246   if (kill_x != -1 || kill_y != -1)
13247   {
13248     if (IS_PLAYER(kill_x, kill_y))
13249     {
13250       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13251
13252       if (player->shield_deadly_time_left > 0 &&
13253           !IS_INDESTRUCTIBLE(bad_element))
13254         Bang(bad_x, bad_y);
13255       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13256         KillPlayer(player);
13257     }
13258     else
13259       Bang(kill_x, kill_y);
13260   }
13261 }
13262
13263 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13264 {
13265   int bad_element = Feld[bad_x][bad_y];
13266   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13267   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13268   int test_x = bad_x + dx, test_y = bad_y + dy;
13269   int test_move_dir, test_element;
13270   int kill_x = -1, kill_y = -1;
13271
13272   if (!IN_LEV_FIELD(test_x, test_y))
13273     return;
13274
13275   test_move_dir =
13276     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13277
13278   test_element = Feld[test_x][test_y];
13279
13280   if (test_move_dir != bad_move_dir)
13281   {
13282     /* good thing can be player or penguin that does not move away */
13283     if (IS_PLAYER(test_x, test_y))
13284     {
13285       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13286
13287       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13288          player as being hit when he is moving towards the bad thing, because
13289          the "get hit by" condition would be lost after the player stops) */
13290       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13291         return;         /* player moves away from bad thing */
13292
13293       kill_x = test_x;
13294       kill_y = test_y;
13295     }
13296     else if (test_element == EL_PENGUIN)
13297     {
13298       kill_x = test_x;
13299       kill_y = test_y;
13300     }
13301   }
13302
13303   if (kill_x != -1 || kill_y != -1)
13304   {
13305     if (IS_PLAYER(kill_x, kill_y))
13306     {
13307       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13308
13309       if (player->shield_deadly_time_left > 0 &&
13310           !IS_INDESTRUCTIBLE(bad_element))
13311         Bang(bad_x, bad_y);
13312       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13313         KillPlayer(player);
13314     }
13315     else
13316       Bang(kill_x, kill_y);
13317   }
13318 }
13319
13320 void TestIfPlayerTouchesBadThing(int x, int y)
13321 {
13322   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13323 }
13324
13325 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13326 {
13327   TestIfGoodThingHitsBadThing(x, y, move_dir);
13328 }
13329
13330 void TestIfBadThingTouchesPlayer(int x, int y)
13331 {
13332   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13333 }
13334
13335 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13336 {
13337   TestIfBadThingHitsGoodThing(x, y, move_dir);
13338 }
13339
13340 void TestIfFriendTouchesBadThing(int x, int y)
13341 {
13342   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13343 }
13344
13345 void TestIfBadThingTouchesFriend(int x, int y)
13346 {
13347   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13348 }
13349
13350 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13351 {
13352   int i, kill_x = bad_x, kill_y = bad_y;
13353   static int xy[4][2] =
13354   {
13355     { 0, -1 },
13356     { -1, 0 },
13357     { +1, 0 },
13358     { 0, +1 }
13359   };
13360
13361   for (i = 0; i < NUM_DIRECTIONS; i++)
13362   {
13363     int x, y, element;
13364
13365     x = bad_x + xy[i][0];
13366     y = bad_y + xy[i][1];
13367     if (!IN_LEV_FIELD(x, y))
13368       continue;
13369
13370     element = Feld[x][y];
13371     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13372         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13373     {
13374       kill_x = x;
13375       kill_y = y;
13376       break;
13377     }
13378   }
13379
13380   if (kill_x != bad_x || kill_y != bad_y)
13381     Bang(bad_x, bad_y);
13382 }
13383
13384 void KillPlayer(struct PlayerInfo *player)
13385 {
13386   int jx = player->jx, jy = player->jy;
13387
13388   if (!player->active)
13389     return;
13390
13391 #if 0
13392   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13393          player->killed, player->active, player->reanimated);
13394 #endif
13395
13396   /* the following code was introduced to prevent an infinite loop when calling
13397      -> Bang()
13398      -> CheckTriggeredElementChangeExt()
13399      -> ExecuteCustomElementAction()
13400      -> KillPlayer()
13401      -> (infinitely repeating the above sequence of function calls)
13402      which occurs when killing the player while having a CE with the setting
13403      "kill player X when explosion of <player X>"; the solution using a new
13404      field "player->killed" was chosen for backwards compatibility, although
13405      clever use of the fields "player->active" etc. would probably also work */
13406 #if 1
13407   if (player->killed)
13408     return;
13409 #endif
13410
13411   player->killed = TRUE;
13412
13413   /* remove accessible field at the player's position */
13414   Feld[jx][jy] = EL_EMPTY;
13415
13416   /* deactivate shield (else Bang()/Explode() would not work right) */
13417   player->shield_normal_time_left = 0;
13418   player->shield_deadly_time_left = 0;
13419
13420 #if 0
13421   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13422          player->killed, player->active, player->reanimated);
13423 #endif
13424
13425   Bang(jx, jy);
13426
13427 #if 0
13428   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13429          player->killed, player->active, player->reanimated);
13430 #endif
13431
13432   if (player->reanimated)       /* killed player may have been reanimated */
13433     player->killed = player->reanimated = FALSE;
13434   else
13435     BuryPlayer(player);
13436 }
13437
13438 static void KillPlayerUnlessEnemyProtected(int x, int y)
13439 {
13440   if (!PLAYER_ENEMY_PROTECTED(x, y))
13441     KillPlayer(PLAYERINFO(x, y));
13442 }
13443
13444 static void KillPlayerUnlessExplosionProtected(int x, int y)
13445 {
13446   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13447     KillPlayer(PLAYERINFO(x, y));
13448 }
13449
13450 void BuryPlayer(struct PlayerInfo *player)
13451 {
13452   int jx = player->jx, jy = player->jy;
13453
13454   if (!player->active)
13455     return;
13456
13457   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13458   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13459
13460   player->GameOver = TRUE;
13461   RemovePlayer(player);
13462 }
13463
13464 void RemovePlayer(struct PlayerInfo *player)
13465 {
13466   int jx = player->jx, jy = player->jy;
13467   int i, found = FALSE;
13468
13469   player->present = FALSE;
13470   player->active = FALSE;
13471
13472   if (!ExplodeField[jx][jy])
13473     StorePlayer[jx][jy] = 0;
13474
13475   if (player->is_moving)
13476     TEST_DrawLevelField(player->last_jx, player->last_jy);
13477
13478   for (i = 0; i < MAX_PLAYERS; i++)
13479     if (stored_player[i].active)
13480       found = TRUE;
13481
13482   if (!found)
13483     AllPlayersGone = TRUE;
13484
13485   ExitX = ZX = jx;
13486   ExitY = ZY = jy;
13487 }
13488
13489 static void setFieldForSnapping(int x, int y, int element, int direction)
13490 {
13491   struct ElementInfo *ei = &element_info[element];
13492   int direction_bit = MV_DIR_TO_BIT(direction);
13493   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13494   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13495                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13496
13497   Feld[x][y] = EL_ELEMENT_SNAPPING;
13498   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13499
13500   ResetGfxAnimation(x, y);
13501
13502   GfxElement[x][y] = element;
13503   GfxAction[x][y] = action;
13504   GfxDir[x][y] = direction;
13505   GfxFrame[x][y] = -1;
13506 }
13507
13508 /*
13509   =============================================================================
13510   checkDiagonalPushing()
13511   -----------------------------------------------------------------------------
13512   check if diagonal input device direction results in pushing of object
13513   (by checking if the alternative direction is walkable, diggable, ...)
13514   =============================================================================
13515 */
13516
13517 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13518                                     int x, int y, int real_dx, int real_dy)
13519 {
13520   int jx, jy, dx, dy, xx, yy;
13521
13522   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13523     return TRUE;
13524
13525   /* diagonal direction: check alternative direction */
13526   jx = player->jx;
13527   jy = player->jy;
13528   dx = x - jx;
13529   dy = y - jy;
13530   xx = jx + (dx == 0 ? real_dx : 0);
13531   yy = jy + (dy == 0 ? real_dy : 0);
13532
13533   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13534 }
13535
13536 /*
13537   =============================================================================
13538   DigField()
13539   -----------------------------------------------------------------------------
13540   x, y:                 field next to player (non-diagonal) to try to dig to
13541   real_dx, real_dy:     direction as read from input device (can be diagonal)
13542   =============================================================================
13543 */
13544
13545 static int DigField(struct PlayerInfo *player,
13546                     int oldx, int oldy, int x, int y,
13547                     int real_dx, int real_dy, int mode)
13548 {
13549   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13550   boolean player_was_pushing = player->is_pushing;
13551   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13552   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13553   int jx = oldx, jy = oldy;
13554   int dx = x - jx, dy = y - jy;
13555   int nextx = x + dx, nexty = y + dy;
13556   int move_direction = (dx == -1 ? MV_LEFT  :
13557                         dx == +1 ? MV_RIGHT :
13558                         dy == -1 ? MV_UP    :
13559                         dy == +1 ? MV_DOWN  : MV_NONE);
13560   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13561   int dig_side = MV_DIR_OPPOSITE(move_direction);
13562   int old_element = Feld[jx][jy];
13563   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13564   int collect_count;
13565
13566   if (is_player)                /* function can also be called by EL_PENGUIN */
13567   {
13568     if (player->MovPos == 0)
13569     {
13570       player->is_digging = FALSE;
13571       player->is_collecting = FALSE;
13572     }
13573
13574     if (player->MovPos == 0)    /* last pushing move finished */
13575       player->is_pushing = FALSE;
13576
13577     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13578     {
13579       player->is_switching = FALSE;
13580       player->push_delay = -1;
13581
13582       return MP_NO_ACTION;
13583     }
13584   }
13585
13586   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13587     old_element = Back[jx][jy];
13588
13589   /* in case of element dropped at player position, check background */
13590   else if (Back[jx][jy] != EL_EMPTY &&
13591            game.engine_version >= VERSION_IDENT(2,2,0,0))
13592     old_element = Back[jx][jy];
13593
13594   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13595     return MP_NO_ACTION;        /* field has no opening in this direction */
13596
13597   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13598     return MP_NO_ACTION;        /* field has no opening in this direction */
13599
13600   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13601   {
13602     SplashAcid(x, y);
13603
13604     Feld[jx][jy] = player->artwork_element;
13605     InitMovingField(jx, jy, MV_DOWN);
13606     Store[jx][jy] = EL_ACID;
13607     ContinueMoving(jx, jy);
13608     BuryPlayer(player);
13609
13610     return MP_DONT_RUN_INTO;
13611   }
13612
13613   if (player_can_move && DONT_RUN_INTO(element))
13614   {
13615     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13616
13617     return MP_DONT_RUN_INTO;
13618   }
13619
13620   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13621     return MP_NO_ACTION;
13622
13623   collect_count = element_info[element].collect_count_initial;
13624
13625   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13626     return MP_NO_ACTION;
13627
13628   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13629     player_can_move = player_can_move_or_snap;
13630
13631   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13632       game.engine_version >= VERSION_IDENT(2,2,0,0))
13633   {
13634     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13635                                player->index_bit, dig_side);
13636     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13637                                         player->index_bit, dig_side);
13638
13639     if (element == EL_DC_LANDMINE)
13640       Bang(x, y);
13641
13642     if (Feld[x][y] != element)          /* field changed by snapping */
13643       return MP_ACTION;
13644
13645     return MP_NO_ACTION;
13646   }
13647
13648   if (player->gravity && is_player && !player->is_auto_moving &&
13649       canFallDown(player) && move_direction != MV_DOWN &&
13650       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13651     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13652
13653   if (player_can_move &&
13654       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13655   {
13656     int sound_element = SND_ELEMENT(element);
13657     int sound_action = ACTION_WALKING;
13658
13659     if (IS_RND_GATE(element))
13660     {
13661       if (!player->key[RND_GATE_NR(element)])
13662         return MP_NO_ACTION;
13663     }
13664     else if (IS_RND_GATE_GRAY(element))
13665     {
13666       if (!player->key[RND_GATE_GRAY_NR(element)])
13667         return MP_NO_ACTION;
13668     }
13669     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13670     {
13671       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13672         return MP_NO_ACTION;
13673     }
13674     else if (element == EL_EXIT_OPEN ||
13675              element == EL_EM_EXIT_OPEN ||
13676              element == EL_EM_EXIT_OPENING ||
13677              element == EL_STEEL_EXIT_OPEN ||
13678              element == EL_EM_STEEL_EXIT_OPEN ||
13679              element == EL_EM_STEEL_EXIT_OPENING ||
13680              element == EL_SP_EXIT_OPEN ||
13681              element == EL_SP_EXIT_OPENING)
13682     {
13683       sound_action = ACTION_PASSING;    /* player is passing exit */
13684     }
13685     else if (element == EL_EMPTY)
13686     {
13687       sound_action = ACTION_MOVING;             /* nothing to walk on */
13688     }
13689
13690     /* play sound from background or player, whatever is available */
13691     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13692       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13693     else
13694       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13695   }
13696   else if (player_can_move &&
13697            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13698   {
13699     if (!ACCESS_FROM(element, opposite_direction))
13700       return MP_NO_ACTION;      /* field not accessible from this direction */
13701
13702     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13703       return MP_NO_ACTION;
13704
13705     if (IS_EM_GATE(element))
13706     {
13707       if (!player->key[EM_GATE_NR(element)])
13708         return MP_NO_ACTION;
13709     }
13710     else if (IS_EM_GATE_GRAY(element))
13711     {
13712       if (!player->key[EM_GATE_GRAY_NR(element)])
13713         return MP_NO_ACTION;
13714     }
13715     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13716     {
13717       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13718         return MP_NO_ACTION;
13719     }
13720     else if (IS_EMC_GATE(element))
13721     {
13722       if (!player->key[EMC_GATE_NR(element)])
13723         return MP_NO_ACTION;
13724     }
13725     else if (IS_EMC_GATE_GRAY(element))
13726     {
13727       if (!player->key[EMC_GATE_GRAY_NR(element)])
13728         return MP_NO_ACTION;
13729     }
13730     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13731     {
13732       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13733         return MP_NO_ACTION;
13734     }
13735     else if (element == EL_DC_GATE_WHITE ||
13736              element == EL_DC_GATE_WHITE_GRAY ||
13737              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13738     {
13739       if (player->num_white_keys == 0)
13740         return MP_NO_ACTION;
13741
13742       player->num_white_keys--;
13743     }
13744     else if (IS_SP_PORT(element))
13745     {
13746       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13747           element == EL_SP_GRAVITY_PORT_RIGHT ||
13748           element == EL_SP_GRAVITY_PORT_UP ||
13749           element == EL_SP_GRAVITY_PORT_DOWN)
13750         player->gravity = !player->gravity;
13751       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13752                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13753                element == EL_SP_GRAVITY_ON_PORT_UP ||
13754                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13755         player->gravity = TRUE;
13756       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13757                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13758                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13759                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13760         player->gravity = FALSE;
13761     }
13762
13763     /* automatically move to the next field with double speed */
13764     player->programmed_action = move_direction;
13765
13766     if (player->move_delay_reset_counter == 0)
13767     {
13768       player->move_delay_reset_counter = 2;     /* two double speed steps */
13769
13770       DOUBLE_PLAYER_SPEED(player);
13771     }
13772
13773     PlayLevelSoundAction(x, y, ACTION_PASSING);
13774   }
13775   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13776   {
13777     RemoveField(x, y);
13778
13779     if (mode != DF_SNAP)
13780     {
13781       GfxElement[x][y] = GFX_ELEMENT(element);
13782       player->is_digging = TRUE;
13783     }
13784
13785     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13786
13787     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13788                                         player->index_bit, dig_side);
13789
13790     if (mode == DF_SNAP)
13791     {
13792       if (level.block_snap_field)
13793         setFieldForSnapping(x, y, element, move_direction);
13794       else
13795         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13796
13797       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13798                                           player->index_bit, dig_side);
13799     }
13800   }
13801   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13802   {
13803     RemoveField(x, y);
13804
13805     if (is_player && mode != DF_SNAP)
13806     {
13807       GfxElement[x][y] = element;
13808       player->is_collecting = TRUE;
13809     }
13810
13811     if (element == EL_SPEED_PILL)
13812     {
13813       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13814     }
13815     else if (element == EL_EXTRA_TIME && level.time > 0)
13816     {
13817       TimeLeft += level.extra_time;
13818
13819       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13820
13821       DisplayGameControlValues();
13822     }
13823     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13824     {
13825       player->shield_normal_time_left += level.shield_normal_time;
13826       if (element == EL_SHIELD_DEADLY)
13827         player->shield_deadly_time_left += level.shield_deadly_time;
13828     }
13829     else if (element == EL_DYNAMITE ||
13830              element == EL_EM_DYNAMITE ||
13831              element == EL_SP_DISK_RED)
13832     {
13833       if (player->inventory_size < MAX_INVENTORY_SIZE)
13834         player->inventory_element[player->inventory_size++] = element;
13835
13836       DrawGameDoorValues();
13837     }
13838     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13839     {
13840       player->dynabomb_count++;
13841       player->dynabombs_left++;
13842     }
13843     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13844     {
13845       player->dynabomb_size++;
13846     }
13847     else if (element == EL_DYNABOMB_INCREASE_POWER)
13848     {
13849       player->dynabomb_xl = TRUE;
13850     }
13851     else if (IS_KEY(element))
13852     {
13853       player->key[KEY_NR(element)] = TRUE;
13854
13855       DrawGameDoorValues();
13856     }
13857     else if (element == EL_DC_KEY_WHITE)
13858     {
13859       player->num_white_keys++;
13860
13861       /* display white keys? */
13862       /* DrawGameDoorValues(); */
13863     }
13864     else if (IS_ENVELOPE(element))
13865     {
13866       player->show_envelope = element;
13867     }
13868     else if (element == EL_EMC_LENSES)
13869     {
13870       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13871
13872       RedrawAllInvisibleElementsForLenses();
13873     }
13874     else if (element == EL_EMC_MAGNIFIER)
13875     {
13876       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13877
13878       RedrawAllInvisibleElementsForMagnifier();
13879     }
13880     else if (IS_DROPPABLE(element) ||
13881              IS_THROWABLE(element))     /* can be collected and dropped */
13882     {
13883       int i;
13884
13885       if (collect_count == 0)
13886         player->inventory_infinite_element = element;
13887       else
13888         for (i = 0; i < collect_count; i++)
13889           if (player->inventory_size < MAX_INVENTORY_SIZE)
13890             player->inventory_element[player->inventory_size++] = element;
13891
13892       DrawGameDoorValues();
13893     }
13894     else if (collect_count > 0)
13895     {
13896       local_player->gems_still_needed -= collect_count;
13897       if (local_player->gems_still_needed < 0)
13898         local_player->gems_still_needed = 0;
13899
13900       game.snapshot.collected_item = TRUE;
13901
13902       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13903
13904       DisplayGameControlValues();
13905     }
13906
13907     RaiseScoreElement(element);
13908     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13909
13910     if (is_player)
13911       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13912                                           player->index_bit, dig_side);
13913
13914     if (mode == DF_SNAP)
13915     {
13916       if (level.block_snap_field)
13917         setFieldForSnapping(x, y, element, move_direction);
13918       else
13919         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13920
13921       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13922                                           player->index_bit, dig_side);
13923     }
13924   }
13925   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13926   {
13927     if (mode == DF_SNAP && element != EL_BD_ROCK)
13928       return MP_NO_ACTION;
13929
13930     if (CAN_FALL(element) && dy)
13931       return MP_NO_ACTION;
13932
13933     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13934         !(element == EL_SPRING && level.use_spring_bug))
13935       return MP_NO_ACTION;
13936
13937     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13938         ((move_direction & MV_VERTICAL &&
13939           ((element_info[element].move_pattern & MV_LEFT &&
13940             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13941            (element_info[element].move_pattern & MV_RIGHT &&
13942             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13943          (move_direction & MV_HORIZONTAL &&
13944           ((element_info[element].move_pattern & MV_UP &&
13945             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13946            (element_info[element].move_pattern & MV_DOWN &&
13947             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13948       return MP_NO_ACTION;
13949
13950     /* do not push elements already moving away faster than player */
13951     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13952         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13953       return MP_NO_ACTION;
13954
13955     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13956     {
13957       if (player->push_delay_value == -1 || !player_was_pushing)
13958         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13959     }
13960     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13961     {
13962       if (player->push_delay_value == -1)
13963         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13964     }
13965     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13966     {
13967       if (!player->is_pushing)
13968         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13969     }
13970
13971     player->is_pushing = TRUE;
13972     player->is_active = TRUE;
13973
13974     if (!(IN_LEV_FIELD(nextx, nexty) &&
13975           (IS_FREE(nextx, nexty) ||
13976            (IS_SB_ELEMENT(element) &&
13977             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13978            (IS_CUSTOM_ELEMENT(element) &&
13979             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13980       return MP_NO_ACTION;
13981
13982     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13983       return MP_NO_ACTION;
13984
13985     if (player->push_delay == -1)       /* new pushing; restart delay */
13986       player->push_delay = 0;
13987
13988     if (player->push_delay < player->push_delay_value &&
13989         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13990         element != EL_SPRING && element != EL_BALLOON)
13991     {
13992       /* make sure that there is no move delay before next try to push */
13993       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13994         player->move_delay = 0;
13995
13996       return MP_NO_ACTION;
13997     }
13998
13999     if (IS_CUSTOM_ELEMENT(element) &&
14000         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14001     {
14002       if (!DigFieldByCE(nextx, nexty, element))
14003         return MP_NO_ACTION;
14004     }
14005
14006     if (IS_SB_ELEMENT(element))
14007     {
14008       if (element == EL_SOKOBAN_FIELD_FULL)
14009       {
14010         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14011         local_player->sokobanfields_still_needed++;
14012       }
14013
14014       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14015       {
14016         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14017         local_player->sokobanfields_still_needed--;
14018       }
14019
14020       Feld[x][y] = EL_SOKOBAN_OBJECT;
14021
14022       if (Back[x][y] == Back[nextx][nexty])
14023         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14024       else if (Back[x][y] != 0)
14025         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14026                                     ACTION_EMPTYING);
14027       else
14028         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14029                                     ACTION_FILLING);
14030
14031       if (local_player->sokobanfields_still_needed == 0 &&
14032           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14033       {
14034         PlayerWins(player);
14035
14036         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14037       }
14038     }
14039     else
14040       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14041
14042     InitMovingField(x, y, move_direction);
14043     GfxAction[x][y] = ACTION_PUSHING;
14044
14045     if (mode == DF_SNAP)
14046       ContinueMoving(x, y);
14047     else
14048       MovPos[x][y] = (dx != 0 ? dx : dy);
14049
14050     Pushed[x][y] = TRUE;
14051     Pushed[nextx][nexty] = TRUE;
14052
14053     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14054       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14055     else
14056       player->push_delay_value = -1;    /* get new value later */
14057
14058     /* check for element change _after_ element has been pushed */
14059     if (game.use_change_when_pushing_bug)
14060     {
14061       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14062                                  player->index_bit, dig_side);
14063       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14064                                           player->index_bit, dig_side);
14065     }
14066   }
14067   else if (IS_SWITCHABLE(element))
14068   {
14069     if (PLAYER_SWITCHING(player, x, y))
14070     {
14071       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14072                                           player->index_bit, dig_side);
14073
14074       return MP_ACTION;
14075     }
14076
14077     player->is_switching = TRUE;
14078     player->switch_x = x;
14079     player->switch_y = y;
14080
14081     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14082
14083     if (element == EL_ROBOT_WHEEL)
14084     {
14085       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14086       ZX = x;
14087       ZY = y;
14088
14089       game.robot_wheel_active = TRUE;
14090
14091       TEST_DrawLevelField(x, y);
14092     }
14093     else if (element == EL_SP_TERMINAL)
14094     {
14095       int xx, yy;
14096
14097       SCAN_PLAYFIELD(xx, yy)
14098       {
14099         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14100         {
14101           Bang(xx, yy);
14102         }
14103         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14104         {
14105           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14106
14107           ResetGfxAnimation(xx, yy);
14108           TEST_DrawLevelField(xx, yy);
14109         }
14110       }
14111     }
14112     else if (IS_BELT_SWITCH(element))
14113     {
14114       ToggleBeltSwitch(x, y);
14115     }
14116     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14117              element == EL_SWITCHGATE_SWITCH_DOWN ||
14118              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14119              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14120     {
14121       ToggleSwitchgateSwitch(x, y);
14122     }
14123     else if (element == EL_LIGHT_SWITCH ||
14124              element == EL_LIGHT_SWITCH_ACTIVE)
14125     {
14126       ToggleLightSwitch(x, y);
14127     }
14128     else if (element == EL_TIMEGATE_SWITCH ||
14129              element == EL_DC_TIMEGATE_SWITCH)
14130     {
14131       ActivateTimegateSwitch(x, y);
14132     }
14133     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14134              element == EL_BALLOON_SWITCH_RIGHT ||
14135              element == EL_BALLOON_SWITCH_UP    ||
14136              element == EL_BALLOON_SWITCH_DOWN  ||
14137              element == EL_BALLOON_SWITCH_NONE  ||
14138              element == EL_BALLOON_SWITCH_ANY)
14139     {
14140       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14141                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14142                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14143                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14144                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14145                              move_direction);
14146     }
14147     else if (element == EL_LAMP)
14148     {
14149       Feld[x][y] = EL_LAMP_ACTIVE;
14150       local_player->lights_still_needed--;
14151
14152       ResetGfxAnimation(x, y);
14153       TEST_DrawLevelField(x, y);
14154     }
14155     else if (element == EL_TIME_ORB_FULL)
14156     {
14157       Feld[x][y] = EL_TIME_ORB_EMPTY;
14158
14159       if (level.time > 0 || level.use_time_orb_bug)
14160       {
14161         TimeLeft += level.time_orb_time;
14162         game.no_time_limit = FALSE;
14163
14164         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14165
14166         DisplayGameControlValues();
14167       }
14168
14169       ResetGfxAnimation(x, y);
14170       TEST_DrawLevelField(x, y);
14171     }
14172     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14173              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14174     {
14175       int xx, yy;
14176
14177       game.ball_state = !game.ball_state;
14178
14179       SCAN_PLAYFIELD(xx, yy)
14180       {
14181         int e = Feld[xx][yy];
14182
14183         if (game.ball_state)
14184         {
14185           if (e == EL_EMC_MAGIC_BALL)
14186             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14187           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14188             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14189         }
14190         else
14191         {
14192           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14193             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14194           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14195             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14196         }
14197       }
14198     }
14199
14200     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14201                                         player->index_bit, dig_side);
14202
14203     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14204                                         player->index_bit, dig_side);
14205
14206     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14207                                         player->index_bit, dig_side);
14208
14209     return MP_ACTION;
14210   }
14211   else
14212   {
14213     if (!PLAYER_SWITCHING(player, x, y))
14214     {
14215       player->is_switching = TRUE;
14216       player->switch_x = x;
14217       player->switch_y = y;
14218
14219       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14220                                  player->index_bit, dig_side);
14221       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14222                                           player->index_bit, dig_side);
14223
14224       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14225                                  player->index_bit, dig_side);
14226       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14227                                           player->index_bit, dig_side);
14228     }
14229
14230     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14231                                player->index_bit, dig_side);
14232     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14233                                         player->index_bit, dig_side);
14234
14235     return MP_NO_ACTION;
14236   }
14237
14238   player->push_delay = -1;
14239
14240   if (is_player)                /* function can also be called by EL_PENGUIN */
14241   {
14242     if (Feld[x][y] != element)          /* really digged/collected something */
14243     {
14244       player->is_collecting = !player->is_digging;
14245       player->is_active = TRUE;
14246     }
14247   }
14248
14249   return MP_MOVING;
14250 }
14251
14252 static boolean DigFieldByCE(int x, int y, int digging_element)
14253 {
14254   int element = Feld[x][y];
14255
14256   if (!IS_FREE(x, y))
14257   {
14258     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14259                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14260                   ACTION_BREAKING);
14261
14262     /* no element can dig solid indestructible elements */
14263     if (IS_INDESTRUCTIBLE(element) &&
14264         !IS_DIGGABLE(element) &&
14265         !IS_COLLECTIBLE(element))
14266       return FALSE;
14267
14268     if (AmoebaNr[x][y] &&
14269         (element == EL_AMOEBA_FULL ||
14270          element == EL_BD_AMOEBA ||
14271          element == EL_AMOEBA_GROWING))
14272     {
14273       AmoebaCnt[AmoebaNr[x][y]]--;
14274       AmoebaCnt2[AmoebaNr[x][y]]--;
14275     }
14276
14277     if (IS_MOVING(x, y))
14278       RemoveMovingField(x, y);
14279     else
14280     {
14281       RemoveField(x, y);
14282       TEST_DrawLevelField(x, y);
14283     }
14284
14285     /* if digged element was about to explode, prevent the explosion */
14286     ExplodeField[x][y] = EX_TYPE_NONE;
14287
14288     PlayLevelSoundAction(x, y, action);
14289   }
14290
14291   Store[x][y] = EL_EMPTY;
14292
14293   /* this makes it possible to leave the removed element again */
14294   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14295     Store[x][y] = element;
14296
14297   return TRUE;
14298 }
14299
14300 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14301 {
14302   int jx = player->jx, jy = player->jy;
14303   int x = jx + dx, y = jy + dy;
14304   int snap_direction = (dx == -1 ? MV_LEFT  :
14305                         dx == +1 ? MV_RIGHT :
14306                         dy == -1 ? MV_UP    :
14307                         dy == +1 ? MV_DOWN  : MV_NONE);
14308   boolean can_continue_snapping = (level.continuous_snapping &&
14309                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14310
14311   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14312     return FALSE;
14313
14314   if (!player->active || !IN_LEV_FIELD(x, y))
14315     return FALSE;
14316
14317   if (dx && dy)
14318     return FALSE;
14319
14320   if (!dx && !dy)
14321   {
14322     if (player->MovPos == 0)
14323       player->is_pushing = FALSE;
14324
14325     player->is_snapping = FALSE;
14326
14327     if (player->MovPos == 0)
14328     {
14329       player->is_moving = FALSE;
14330       player->is_digging = FALSE;
14331       player->is_collecting = FALSE;
14332     }
14333
14334     return FALSE;
14335   }
14336
14337   /* prevent snapping with already pressed snap key when not allowed */
14338   if (player->is_snapping && !can_continue_snapping)
14339     return FALSE;
14340
14341   player->MovDir = snap_direction;
14342
14343   if (player->MovPos == 0)
14344   {
14345     player->is_moving = FALSE;
14346     player->is_digging = FALSE;
14347     player->is_collecting = FALSE;
14348   }
14349
14350   player->is_dropping = FALSE;
14351   player->is_dropping_pressed = FALSE;
14352   player->drop_pressed_delay = 0;
14353
14354   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14355     return FALSE;
14356
14357   player->is_snapping = TRUE;
14358   player->is_active = TRUE;
14359
14360   if (player->MovPos == 0)
14361   {
14362     player->is_moving = FALSE;
14363     player->is_digging = FALSE;
14364     player->is_collecting = FALSE;
14365   }
14366
14367   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14368     TEST_DrawLevelField(player->last_jx, player->last_jy);
14369
14370   TEST_DrawLevelField(x, y);
14371
14372   return TRUE;
14373 }
14374
14375 static boolean DropElement(struct PlayerInfo *player)
14376 {
14377   int old_element, new_element;
14378   int dropx = player->jx, dropy = player->jy;
14379   int drop_direction = player->MovDir;
14380   int drop_side = drop_direction;
14381   int drop_element = get_next_dropped_element(player);
14382
14383   /* do not drop an element on top of another element; when holding drop key
14384      pressed without moving, dropped element must move away before the next
14385      element can be dropped (this is especially important if the next element
14386      is dynamite, which can be placed on background for historical reasons) */
14387   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14388     return MP_ACTION;
14389
14390   if (IS_THROWABLE(drop_element))
14391   {
14392     dropx += GET_DX_FROM_DIR(drop_direction);
14393     dropy += GET_DY_FROM_DIR(drop_direction);
14394
14395     if (!IN_LEV_FIELD(dropx, dropy))
14396       return FALSE;
14397   }
14398
14399   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14400   new_element = drop_element;           /* default: no change when dropping */
14401
14402   /* check if player is active, not moving and ready to drop */
14403   if (!player->active || player->MovPos || player->drop_delay > 0)
14404     return FALSE;
14405
14406   /* check if player has anything that can be dropped */
14407   if (new_element == EL_UNDEFINED)
14408     return FALSE;
14409
14410   /* only set if player has anything that can be dropped */
14411   player->is_dropping_pressed = TRUE;
14412
14413   /* check if drop key was pressed long enough for EM style dynamite */
14414   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14415     return FALSE;
14416
14417   /* check if anything can be dropped at the current position */
14418   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14419     return FALSE;
14420
14421   /* collected custom elements can only be dropped on empty fields */
14422   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14423     return FALSE;
14424
14425   if (old_element != EL_EMPTY)
14426     Back[dropx][dropy] = old_element;   /* store old element on this field */
14427
14428   ResetGfxAnimation(dropx, dropy);
14429   ResetRandomAnimationValue(dropx, dropy);
14430
14431   if (player->inventory_size > 0 ||
14432       player->inventory_infinite_element != EL_UNDEFINED)
14433   {
14434     if (player->inventory_size > 0)
14435     {
14436       player->inventory_size--;
14437
14438       DrawGameDoorValues();
14439
14440       if (new_element == EL_DYNAMITE)
14441         new_element = EL_DYNAMITE_ACTIVE;
14442       else if (new_element == EL_EM_DYNAMITE)
14443         new_element = EL_EM_DYNAMITE_ACTIVE;
14444       else if (new_element == EL_SP_DISK_RED)
14445         new_element = EL_SP_DISK_RED_ACTIVE;
14446     }
14447
14448     Feld[dropx][dropy] = new_element;
14449
14450     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14451       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14452                           el2img(Feld[dropx][dropy]), 0);
14453
14454     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14455
14456     /* needed if previous element just changed to "empty" in the last frame */
14457     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14458
14459     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14460                                player->index_bit, drop_side);
14461     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14462                                         CE_PLAYER_DROPS_X,
14463                                         player->index_bit, drop_side);
14464
14465     TestIfElementTouchesCustomElement(dropx, dropy);
14466   }
14467   else          /* player is dropping a dyna bomb */
14468   {
14469     player->dynabombs_left--;
14470
14471     Feld[dropx][dropy] = new_element;
14472
14473     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14474       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14475                           el2img(Feld[dropx][dropy]), 0);
14476
14477     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14478   }
14479
14480   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14481     InitField_WithBug1(dropx, dropy, FALSE);
14482
14483   new_element = Feld[dropx][dropy];     /* element might have changed */
14484
14485   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14486       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14487   {
14488     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14489       MovDir[dropx][dropy] = drop_direction;
14490
14491     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14492
14493     /* do not cause impact style collision by dropping elements that can fall */
14494     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14495   }
14496
14497   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14498   player->is_dropping = TRUE;
14499
14500   player->drop_pressed_delay = 0;
14501   player->is_dropping_pressed = FALSE;
14502
14503   player->drop_x = dropx;
14504   player->drop_y = dropy;
14505
14506   return TRUE;
14507 }
14508
14509 /* ------------------------------------------------------------------------- */
14510 /* game sound playing functions                                              */
14511 /* ------------------------------------------------------------------------- */
14512
14513 static int *loop_sound_frame = NULL;
14514 static int *loop_sound_volume = NULL;
14515
14516 void InitPlayLevelSound()
14517 {
14518   int num_sounds = getSoundListSize();
14519
14520   checked_free(loop_sound_frame);
14521   checked_free(loop_sound_volume);
14522
14523   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14524   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14525 }
14526
14527 static void PlayLevelSound(int x, int y, int nr)
14528 {
14529   int sx = SCREENX(x), sy = SCREENY(y);
14530   int volume, stereo_position;
14531   int max_distance = 8;
14532   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14533
14534   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14535       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14536     return;
14537
14538   if (!IN_LEV_FIELD(x, y) ||
14539       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14540       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14541     return;
14542
14543   volume = SOUND_MAX_VOLUME;
14544
14545   if (!IN_SCR_FIELD(sx, sy))
14546   {
14547     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14548     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14549
14550     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14551   }
14552
14553   stereo_position = (SOUND_MAX_LEFT +
14554                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14555                      (SCR_FIELDX + 2 * max_distance));
14556
14557   if (IS_LOOP_SOUND(nr))
14558   {
14559     /* This assures that quieter loop sounds do not overwrite louder ones,
14560        while restarting sound volume comparison with each new game frame. */
14561
14562     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14563       return;
14564
14565     loop_sound_volume[nr] = volume;
14566     loop_sound_frame[nr] = FrameCounter;
14567   }
14568
14569   PlaySoundExt(nr, volume, stereo_position, type);
14570 }
14571
14572 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14573 {
14574   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14575                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14576                  y < LEVELY(BY1) ? LEVELY(BY1) :
14577                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14578                  sound_action);
14579 }
14580
14581 static void PlayLevelSoundAction(int x, int y, int action)
14582 {
14583   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14584 }
14585
14586 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14587 {
14588   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14589
14590   if (sound_effect != SND_UNDEFINED)
14591     PlayLevelSound(x, y, sound_effect);
14592 }
14593
14594 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14595                                               int action)
14596 {
14597   int sound_effect = element_info[SND_ELEMENT(element)].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 PlayLevelSoundActionIfLoop(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     PlayLevelSound(x, y, sound_effect);
14609 }
14610
14611 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14612 {
14613   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14614
14615   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14616     StopSound(sound_effect);
14617 }
14618
14619 static int getLevelMusicNr()
14620 {
14621   if (levelset.music[level_nr] != MUS_UNDEFINED)
14622     return levelset.music[level_nr];            /* from config file */
14623   else
14624     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14625 }
14626
14627 static void FadeLevelSounds()
14628 {
14629   FadeSounds();
14630 }
14631
14632 static void FadeLevelMusic()
14633 {
14634   int music_nr = getLevelMusicNr();
14635   char *curr_music = getCurrentlyPlayingMusicFilename();
14636   char *next_music = getMusicInfoEntryFilename(music_nr);
14637
14638   if (!strEqual(curr_music, next_music))
14639     FadeMusic();
14640 }
14641
14642 void FadeLevelSoundsAndMusic()
14643 {
14644   FadeLevelSounds();
14645   FadeLevelMusic();
14646 }
14647
14648 static void PlayLevelMusic()
14649 {
14650   int music_nr = getLevelMusicNr();
14651   char *curr_music = getCurrentlyPlayingMusicFilename();
14652   char *next_music = getMusicInfoEntryFilename(music_nr);
14653
14654   if (!strEqual(curr_music, next_music))
14655     PlayMusic(music_nr);
14656 }
14657
14658 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14659 {
14660   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14661   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14662   int x = xx - 1 - offset;
14663   int y = yy - 1 - offset;
14664
14665   switch (sample)
14666   {
14667     case SAMPLE_blank:
14668       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14669       break;
14670
14671     case SAMPLE_roll:
14672       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14673       break;
14674
14675     case SAMPLE_stone:
14676       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14677       break;
14678
14679     case SAMPLE_nut:
14680       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14681       break;
14682
14683     case SAMPLE_crack:
14684       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14685       break;
14686
14687     case SAMPLE_bug:
14688       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14689       break;
14690
14691     case SAMPLE_tank:
14692       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14693       break;
14694
14695     case SAMPLE_android_clone:
14696       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14697       break;
14698
14699     case SAMPLE_android_move:
14700       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14701       break;
14702
14703     case SAMPLE_spring:
14704       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14705       break;
14706
14707     case SAMPLE_slurp:
14708       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14709       break;
14710
14711     case SAMPLE_eater:
14712       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14713       break;
14714
14715     case SAMPLE_eater_eat:
14716       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14717       break;
14718
14719     case SAMPLE_alien:
14720       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14721       break;
14722
14723     case SAMPLE_collect:
14724       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14725       break;
14726
14727     case SAMPLE_diamond:
14728       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14729       break;
14730
14731     case SAMPLE_squash:
14732       /* !!! CHECK THIS !!! */
14733 #if 1
14734       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14735 #else
14736       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14737 #endif
14738       break;
14739
14740     case SAMPLE_wonderfall:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14742       break;
14743
14744     case SAMPLE_drip:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14746       break;
14747
14748     case SAMPLE_push:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14750       break;
14751
14752     case SAMPLE_dirt:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14754       break;
14755
14756     case SAMPLE_acid:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14758       break;
14759
14760     case SAMPLE_ball:
14761       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14762       break;
14763
14764     case SAMPLE_grow:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14766       break;
14767
14768     case SAMPLE_wonder:
14769       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14770       break;
14771
14772     case SAMPLE_door:
14773       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14774       break;
14775
14776     case SAMPLE_exit_open:
14777       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14778       break;
14779
14780     case SAMPLE_exit_leave:
14781       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14782       break;
14783
14784     case SAMPLE_dynamite:
14785       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14786       break;
14787
14788     case SAMPLE_tick:
14789       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14790       break;
14791
14792     case SAMPLE_press:
14793       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14794       break;
14795
14796     case SAMPLE_wheel:
14797       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14798       break;
14799
14800     case SAMPLE_boom:
14801       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14802       break;
14803
14804     case SAMPLE_die:
14805       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14806       break;
14807
14808     case SAMPLE_time:
14809       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14810       break;
14811
14812     default:
14813       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14814       break;
14815   }
14816 }
14817
14818 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14819 {
14820   int element = map_element_SP_to_RND(element_sp);
14821   int action = map_action_SP_to_RND(action_sp);
14822   int offset = (setup.sp_show_border_elements ? 0 : 1);
14823   int x = xx - offset;
14824   int y = yy - offset;
14825
14826   PlayLevelSoundElementAction(x, y, element, action);
14827 }
14828
14829 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14830 {
14831   int element = map_element_MM_to_RND(element_mm);
14832   int action = map_action_MM_to_RND(action_mm);
14833   int offset = 0;
14834   int x = xx - offset;
14835   int y = yy - offset;
14836
14837   if (!IS_MM_ELEMENT(element))
14838     element = EL_MM_DEFAULT;
14839
14840   PlayLevelSoundElementAction(x, y, element, action);
14841 }
14842
14843 void PlaySound_MM(int sound_mm)
14844 {
14845   int sound = map_sound_MM_to_RND(sound_mm);
14846
14847   if (sound == SND_UNDEFINED)
14848     return;
14849
14850   PlaySound(sound);
14851 }
14852
14853 void PlaySoundLoop_MM(int sound_mm)
14854 {
14855   int sound = map_sound_MM_to_RND(sound_mm);
14856
14857   if (sound == SND_UNDEFINED)
14858     return;
14859
14860   PlaySoundLoop(sound);
14861 }
14862
14863 void StopSound_MM(int sound_mm)
14864 {
14865   int sound = map_sound_MM_to_RND(sound_mm);
14866
14867   if (sound == SND_UNDEFINED)
14868     return;
14869
14870   StopSound(sound);
14871 }
14872
14873 void RaiseScore(int value)
14874 {
14875   local_player->score += value;
14876
14877   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14878
14879   DisplayGameControlValues();
14880 }
14881
14882 void RaiseScoreElement(int element)
14883 {
14884   switch (element)
14885   {
14886     case EL_EMERALD:
14887     case EL_BD_DIAMOND:
14888     case EL_EMERALD_YELLOW:
14889     case EL_EMERALD_RED:
14890     case EL_EMERALD_PURPLE:
14891     case EL_SP_INFOTRON:
14892       RaiseScore(level.score[SC_EMERALD]);
14893       break;
14894     case EL_DIAMOND:
14895       RaiseScore(level.score[SC_DIAMOND]);
14896       break;
14897     case EL_CRYSTAL:
14898       RaiseScore(level.score[SC_CRYSTAL]);
14899       break;
14900     case EL_PEARL:
14901       RaiseScore(level.score[SC_PEARL]);
14902       break;
14903     case EL_BUG:
14904     case EL_BD_BUTTERFLY:
14905     case EL_SP_ELECTRON:
14906       RaiseScore(level.score[SC_BUG]);
14907       break;
14908     case EL_SPACESHIP:
14909     case EL_BD_FIREFLY:
14910     case EL_SP_SNIKSNAK:
14911       RaiseScore(level.score[SC_SPACESHIP]);
14912       break;
14913     case EL_YAMYAM:
14914     case EL_DARK_YAMYAM:
14915       RaiseScore(level.score[SC_YAMYAM]);
14916       break;
14917     case EL_ROBOT:
14918       RaiseScore(level.score[SC_ROBOT]);
14919       break;
14920     case EL_PACMAN:
14921       RaiseScore(level.score[SC_PACMAN]);
14922       break;
14923     case EL_NUT:
14924       RaiseScore(level.score[SC_NUT]);
14925       break;
14926     case EL_DYNAMITE:
14927     case EL_EM_DYNAMITE:
14928     case EL_SP_DISK_RED:
14929     case EL_DYNABOMB_INCREASE_NUMBER:
14930     case EL_DYNABOMB_INCREASE_SIZE:
14931     case EL_DYNABOMB_INCREASE_POWER:
14932       RaiseScore(level.score[SC_DYNAMITE]);
14933       break;
14934     case EL_SHIELD_NORMAL:
14935     case EL_SHIELD_DEADLY:
14936       RaiseScore(level.score[SC_SHIELD]);
14937       break;
14938     case EL_EXTRA_TIME:
14939       RaiseScore(level.extra_time_score);
14940       break;
14941     case EL_KEY_1:
14942     case EL_KEY_2:
14943     case EL_KEY_3:
14944     case EL_KEY_4:
14945     case EL_EM_KEY_1:
14946     case EL_EM_KEY_2:
14947     case EL_EM_KEY_3:
14948     case EL_EM_KEY_4:
14949     case EL_EMC_KEY_5:
14950     case EL_EMC_KEY_6:
14951     case EL_EMC_KEY_7:
14952     case EL_EMC_KEY_8:
14953     case EL_DC_KEY_WHITE:
14954       RaiseScore(level.score[SC_KEY]);
14955       break;
14956     default:
14957       RaiseScore(element_info[element].collect_score);
14958       break;
14959   }
14960 }
14961
14962 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14963 {
14964   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14965   {
14966     /* closing door required in case of envelope style request dialogs */
14967     if (!skip_request)
14968       CloseDoor(DOOR_CLOSE_1);
14969
14970 #if defined(NETWORK_AVALIABLE)
14971     if (network.enabled)
14972       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14973     else
14974 #endif
14975     {
14976       if (quick_quit)
14977         FadeSkipNextFadeIn();
14978
14979       SetGameStatus(GAME_MODE_MAIN);
14980
14981       DrawMainMenu();
14982     }
14983   }
14984   else          /* continue playing the game */
14985   {
14986     if (tape.playing && tape.deactivate_display)
14987       TapeDeactivateDisplayOff(TRUE);
14988
14989     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14990
14991     if (tape.playing && tape.deactivate_display)
14992       TapeDeactivateDisplayOn();
14993   }
14994 }
14995
14996 void RequestQuitGame(boolean ask_if_really_quit)
14997 {
14998   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14999   boolean skip_request = AllPlayersGone || quick_quit;
15000
15001   RequestQuitGameExt(skip_request, quick_quit,
15002                      "Do you really want to quit the game?");
15003 }
15004
15005 void RequestRestartGame(char *message)
15006 {
15007   game.restart_game_message = NULL;
15008
15009   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
15010   {
15011     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15012   }
15013   else
15014   {
15015     SetGameStatus(GAME_MODE_MAIN);
15016
15017     DrawMainMenu();
15018   }
15019 }
15020
15021
15022 /* ------------------------------------------------------------------------- */
15023 /* random generator functions                                                */
15024 /* ------------------------------------------------------------------------- */
15025
15026 unsigned int InitEngineRandom_RND(int seed)
15027 {
15028   game.num_random_calls = 0;
15029
15030   return InitEngineRandom(seed);
15031 }
15032
15033 unsigned int RND(int max)
15034 {
15035   if (max > 0)
15036   {
15037     game.num_random_calls++;
15038
15039     return GetEngineRandom(max);
15040   }
15041
15042   return 0;
15043 }
15044
15045
15046 /* ------------------------------------------------------------------------- */
15047 /* game engine snapshot handling functions                                   */
15048 /* ------------------------------------------------------------------------- */
15049
15050 struct EngineSnapshotInfo
15051 {
15052   /* runtime values for custom element collect score */
15053   int collect_score[NUM_CUSTOM_ELEMENTS];
15054
15055   /* runtime values for group element choice position */
15056   int choice_pos[NUM_GROUP_ELEMENTS];
15057
15058   /* runtime values for belt position animations */
15059   int belt_graphic[4][NUM_BELT_PARTS];
15060   int belt_anim_mode[4][NUM_BELT_PARTS];
15061 };
15062
15063 static struct EngineSnapshotInfo engine_snapshot_rnd;
15064 static char *snapshot_level_identifier = NULL;
15065 static int snapshot_level_nr = -1;
15066
15067 static void SaveEngineSnapshotValues_RND()
15068 {
15069   static int belt_base_active_element[4] =
15070   {
15071     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15072     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15073     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15074     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15075   };
15076   int i, j;
15077
15078   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15079   {
15080     int element = EL_CUSTOM_START + i;
15081
15082     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15083   }
15084
15085   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15086   {
15087     int element = EL_GROUP_START + i;
15088
15089     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15090   }
15091
15092   for (i = 0; i < 4; i++)
15093   {
15094     for (j = 0; j < NUM_BELT_PARTS; j++)
15095     {
15096       int element = belt_base_active_element[i] + j;
15097       int graphic = el2img(element);
15098       int anim_mode = graphic_info[graphic].anim_mode;
15099
15100       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15101       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15102     }
15103   }
15104 }
15105
15106 static void LoadEngineSnapshotValues_RND()
15107 {
15108   unsigned int num_random_calls = game.num_random_calls;
15109   int i, j;
15110
15111   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15112   {
15113     int element = EL_CUSTOM_START + i;
15114
15115     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15116   }
15117
15118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15119   {
15120     int element = EL_GROUP_START + i;
15121
15122     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15123   }
15124
15125   for (i = 0; i < 4; i++)
15126   {
15127     for (j = 0; j < NUM_BELT_PARTS; j++)
15128     {
15129       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15130       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15131
15132       graphic_info[graphic].anim_mode = anim_mode;
15133     }
15134   }
15135
15136   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15137   {
15138     InitRND(tape.random_seed);
15139     for (i = 0; i < num_random_calls; i++)
15140       RND(1);
15141   }
15142
15143   if (game.num_random_calls != num_random_calls)
15144   {
15145     Error(ERR_INFO, "number of random calls out of sync");
15146     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15147     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15148     Error(ERR_EXIT, "this should not happen -- please debug");
15149   }
15150 }
15151
15152 void FreeEngineSnapshotSingle()
15153 {
15154   FreeSnapshotSingle();
15155
15156   setString(&snapshot_level_identifier, NULL);
15157   snapshot_level_nr = -1;
15158 }
15159
15160 void FreeEngineSnapshotList()
15161 {
15162   FreeSnapshotList();
15163 }
15164
15165 ListNode *SaveEngineSnapshotBuffers()
15166 {
15167   ListNode *buffers = NULL;
15168
15169   /* copy some special values to a structure better suited for the snapshot */
15170
15171   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15172     SaveEngineSnapshotValues_RND();
15173   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15174     SaveEngineSnapshotValues_EM();
15175   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15176     SaveEngineSnapshotValues_SP(&buffers);
15177   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15178     SaveEngineSnapshotValues_MM(&buffers);
15179
15180   /* save values stored in special snapshot structure */
15181
15182   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15183     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15184   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15185     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15186   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15187     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15188   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15189     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15190
15191   /* save further RND engine values */
15192
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15196
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15201
15202   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15203   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15207
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15211
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15213
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15215
15216   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15218
15219   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15220   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15221   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15222   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15223   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15224   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15225   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15226   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15227   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15228   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15229   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15230   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15231   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15232   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15233   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15234   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15235   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15236   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15237
15238   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15239   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15240
15241   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15242   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15243   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15244
15245   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15246   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15247
15248   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15249   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15250   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15251   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15252   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15253
15254   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15255   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15256
15257 #if 0
15258   ListNode *node = engine_snapshot_list_rnd;
15259   int num_bytes = 0;
15260
15261   while (node != NULL)
15262   {
15263     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15264
15265     node = node->next;
15266   }
15267
15268   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15269 #endif
15270
15271   return buffers;
15272 }
15273
15274 void SaveEngineSnapshotSingle()
15275 {
15276   ListNode *buffers = SaveEngineSnapshotBuffers();
15277
15278   /* finally save all snapshot buffers to single snapshot */
15279   SaveSnapshotSingle(buffers);
15280
15281   /* save level identification information */
15282   setString(&snapshot_level_identifier, leveldir_current->identifier);
15283   snapshot_level_nr = level_nr;
15284 }
15285
15286 boolean CheckSaveEngineSnapshotToList()
15287 {
15288   boolean save_snapshot =
15289     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15290      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15291       game.snapshot.changed_action) ||
15292      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15293       game.snapshot.collected_item));
15294
15295   game.snapshot.changed_action = FALSE;
15296   game.snapshot.collected_item = FALSE;
15297   game.snapshot.save_snapshot = save_snapshot;
15298
15299   return save_snapshot;
15300 }
15301
15302 void SaveEngineSnapshotToList()
15303 {
15304   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15305       tape.quick_resume)
15306     return;
15307
15308   ListNode *buffers = SaveEngineSnapshotBuffers();
15309
15310   /* finally save all snapshot buffers to snapshot list */
15311   SaveSnapshotToList(buffers);
15312 }
15313
15314 void SaveEngineSnapshotToListInitial()
15315 {
15316   FreeEngineSnapshotList();
15317
15318   SaveEngineSnapshotToList();
15319 }
15320
15321 void LoadEngineSnapshotValues()
15322 {
15323   /* restore special values from snapshot structure */
15324
15325   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15326     LoadEngineSnapshotValues_RND();
15327   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15328     LoadEngineSnapshotValues_EM();
15329   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15330     LoadEngineSnapshotValues_SP();
15331   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15332     LoadEngineSnapshotValues_MM();
15333 }
15334
15335 void LoadEngineSnapshotSingle()
15336 {
15337   LoadSnapshotSingle();
15338
15339   LoadEngineSnapshotValues();
15340 }
15341
15342 void LoadEngineSnapshot_Undo(int steps)
15343 {
15344   LoadSnapshotFromList_Older(steps);
15345
15346   LoadEngineSnapshotValues();
15347 }
15348
15349 void LoadEngineSnapshot_Redo(int steps)
15350 {
15351   LoadSnapshotFromList_Newer(steps);
15352
15353   LoadEngineSnapshotValues();
15354 }
15355
15356 boolean CheckEngineSnapshotSingle()
15357 {
15358   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15359           snapshot_level_nr == level_nr);
15360 }
15361
15362 boolean CheckEngineSnapshotList()
15363 {
15364   return CheckSnapshotList();
15365 }
15366
15367
15368 /* ---------- new game button stuff ---------------------------------------- */
15369
15370 static struct
15371 {
15372   int graphic;
15373   struct XY *pos;
15374   int gadget_id;
15375   boolean *setup_value;
15376   boolean allowed_on_tape;
15377   char *infotext;
15378 } gamebutton_info[NUM_GAME_BUTTONS] =
15379 {
15380   {
15381     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15382     GAME_CTRL_ID_STOP,                          NULL,
15383     TRUE,                                       "stop game"
15384   },
15385   {
15386     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15387     GAME_CTRL_ID_PAUSE,                         NULL,
15388     TRUE,                                       "pause game"
15389   },
15390   {
15391     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15392     GAME_CTRL_ID_PLAY,                          NULL,
15393     TRUE,                                       "play game"
15394   },
15395   {
15396     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15397     GAME_CTRL_ID_UNDO,                          NULL,
15398     TRUE,                                       "undo step"
15399   },
15400   {
15401     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15402     GAME_CTRL_ID_REDO,                          NULL,
15403     TRUE,                                       "redo step"
15404   },
15405   {
15406     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15407     GAME_CTRL_ID_SAVE,                          NULL,
15408     TRUE,                                       "save game"
15409   },
15410   {
15411     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15412     GAME_CTRL_ID_PAUSE2,                        NULL,
15413     TRUE,                                       "pause game"
15414   },
15415   {
15416     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15417     GAME_CTRL_ID_LOAD,                          NULL,
15418     TRUE,                                       "load game"
15419   },
15420   {
15421     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15422     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15423     FALSE,                                      "stop game"
15424   },
15425   {
15426     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15427     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15428     FALSE,                                      "pause game"
15429   },
15430   {
15431     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15432     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15433     FALSE,                                      "play game"
15434   },
15435   {
15436     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15437     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15438     TRUE,                                       "background music on/off"
15439   },
15440   {
15441     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15442     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15443     TRUE,                                       "sound loops on/off"
15444   },
15445   {
15446     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15447     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15448     TRUE,                                       "normal sounds on/off"
15449   },
15450   {
15451     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15452     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15453     FALSE,                                      "background music on/off"
15454   },
15455   {
15456     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15457     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15458     FALSE,                                      "sound loops on/off"
15459   },
15460   {
15461     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15462     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15463     FALSE,                                      "normal sounds on/off"
15464   }
15465 };
15466
15467 void CreateGameButtons()
15468 {
15469   int i;
15470
15471   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15472   {
15473     int graphic = gamebutton_info[i].graphic;
15474     struct GraphicInfo *gfx = &graphic_info[graphic];
15475     struct XY *pos = gamebutton_info[i].pos;
15476     struct GadgetInfo *gi;
15477     int button_type;
15478     boolean checked;
15479     unsigned int event_mask;
15480     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15481     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15482     int base_x = (on_tape ? VX : DX);
15483     int base_y = (on_tape ? VY : DY);
15484     int gd_x   = gfx->src_x;
15485     int gd_y   = gfx->src_y;
15486     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15487     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15488     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15489     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15490     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15491     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15492     int id = i;
15493
15494     if (gfx->bitmap == NULL)
15495     {
15496       game_gadget[id] = NULL;
15497
15498       continue;
15499     }
15500
15501     if (id == GAME_CTRL_ID_STOP ||
15502         id == GAME_CTRL_ID_PANEL_STOP ||
15503         id == GAME_CTRL_ID_PLAY ||
15504         id == GAME_CTRL_ID_PANEL_PLAY ||
15505         id == GAME_CTRL_ID_SAVE ||
15506         id == GAME_CTRL_ID_LOAD)
15507     {
15508       button_type = GD_TYPE_NORMAL_BUTTON;
15509       checked = FALSE;
15510       event_mask = GD_EVENT_RELEASED;
15511     }
15512     else if (id == GAME_CTRL_ID_UNDO ||
15513              id == GAME_CTRL_ID_REDO)
15514     {
15515       button_type = GD_TYPE_NORMAL_BUTTON;
15516       checked = FALSE;
15517       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15518     }
15519     else
15520     {
15521       button_type = GD_TYPE_CHECK_BUTTON;
15522       checked = (gamebutton_info[i].setup_value != NULL ?
15523                  *gamebutton_info[i].setup_value : FALSE);
15524       event_mask = GD_EVENT_PRESSED;
15525     }
15526
15527     gi = CreateGadget(GDI_CUSTOM_ID, id,
15528                       GDI_IMAGE_ID, graphic,
15529                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15530                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15531                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15532                       GDI_WIDTH, gfx->width,
15533                       GDI_HEIGHT, gfx->height,
15534                       GDI_TYPE, button_type,
15535                       GDI_STATE, GD_BUTTON_UNPRESSED,
15536                       GDI_CHECKED, checked,
15537                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15538                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15539                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15540                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15541                       GDI_DIRECT_DRAW, FALSE,
15542                       GDI_EVENT_MASK, event_mask,
15543                       GDI_CALLBACK_ACTION, HandleGameButtons,
15544                       GDI_END);
15545
15546     if (gi == NULL)
15547       Error(ERR_EXIT, "cannot create gadget");
15548
15549     game_gadget[id] = gi;
15550   }
15551 }
15552
15553 void FreeGameButtons()
15554 {
15555   int i;
15556
15557   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15558     FreeGadget(game_gadget[i]);
15559 }
15560
15561 static void UnmapGameButtonsAtSamePosition(int id)
15562 {
15563   int i;
15564
15565   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15566     if (i != id &&
15567         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15568         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15569       UnmapGadget(game_gadget[i]);
15570 }
15571
15572 static void UnmapGameButtonsAtSamePosition_All()
15573 {
15574   if (setup.show_snapshot_buttons)
15575   {
15576     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15577     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15578     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15579   }
15580   else
15581   {
15582     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15583     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15584     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15585
15586     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15587     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15588     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15589   }
15590 }
15591
15592 static void MapGameButtonsAtSamePosition(int id)
15593 {
15594   int i;
15595
15596   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15597     if (i != id &&
15598         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15599         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15600       MapGadget(game_gadget[i]);
15601
15602   UnmapGameButtonsAtSamePosition_All();
15603 }
15604
15605 void MapUndoRedoButtons()
15606 {
15607   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15608   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15609
15610   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15611   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15612
15613   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15614 }
15615
15616 void UnmapUndoRedoButtons()
15617 {
15618   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15619   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15620
15621   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15622   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15623
15624   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15625 }
15626
15627 void MapGameButtonsExt(boolean on_tape)
15628 {
15629   int i;
15630
15631   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15632     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15633         i != GAME_CTRL_ID_UNDO &&
15634         i != GAME_CTRL_ID_REDO)
15635       MapGadget(game_gadget[i]);
15636
15637   UnmapGameButtonsAtSamePosition_All();
15638
15639   RedrawGameButtons();
15640 }
15641
15642 void UnmapGameButtonsExt(boolean on_tape)
15643 {
15644   int i;
15645
15646   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15647     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15648       UnmapGadget(game_gadget[i]);
15649 }
15650
15651 void RedrawGameButtonsExt(boolean on_tape)
15652 {
15653   int i;
15654
15655   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15656     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15657       RedrawGadget(game_gadget[i]);
15658
15659   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15660   redraw_mask &= ~REDRAW_ALL;
15661 }
15662
15663 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15664 {
15665   if (gi == NULL)
15666     return;
15667
15668   gi->checked = state;
15669 }
15670
15671 void RedrawSoundButtonGadget(int id)
15672 {
15673   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15674              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15675              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15676              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15677              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15678              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15679              id);
15680
15681   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15682   RedrawGadget(game_gadget[id2]);
15683 }
15684
15685 void MapGameButtons()
15686 {
15687   MapGameButtonsExt(FALSE);
15688 }
15689
15690 void UnmapGameButtons()
15691 {
15692   UnmapGameButtonsExt(FALSE);
15693 }
15694
15695 void RedrawGameButtons()
15696 {
15697   RedrawGameButtonsExt(FALSE);
15698 }
15699
15700 void MapGameButtonsOnTape()
15701 {
15702   MapGameButtonsExt(TRUE);
15703 }
15704
15705 void UnmapGameButtonsOnTape()
15706 {
15707   UnmapGameButtonsExt(TRUE);
15708 }
15709
15710 void RedrawGameButtonsOnTape()
15711 {
15712   RedrawGameButtonsExt(TRUE);
15713 }
15714
15715 void GameUndoRedoExt()
15716 {
15717   ClearPlayerAction();
15718
15719   tape.pausing = TRUE;
15720
15721   RedrawPlayfield();
15722   UpdateAndDisplayGameControlValues();
15723
15724   DrawCompleteVideoDisplay();
15725   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15726   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15727   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15728
15729   BackToFront();
15730 }
15731
15732 void GameUndo(int steps)
15733 {
15734   if (!CheckEngineSnapshotList())
15735     return;
15736
15737   LoadEngineSnapshot_Undo(steps);
15738
15739   GameUndoRedoExt();
15740 }
15741
15742 void GameRedo(int steps)
15743 {
15744   if (!CheckEngineSnapshotList())
15745     return;
15746
15747   LoadEngineSnapshot_Redo(steps);
15748
15749   GameUndoRedoExt();
15750 }
15751
15752 static void HandleGameButtonsExt(int id, int button)
15753 {
15754   static boolean game_undo_executed = FALSE;
15755   int steps = BUTTON_STEPSIZE(button);
15756   boolean handle_game_buttons =
15757     (game_status == GAME_MODE_PLAYING ||
15758      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15759
15760   if (!handle_game_buttons)
15761     return;
15762
15763   switch (id)
15764   {
15765     case GAME_CTRL_ID_STOP:
15766     case GAME_CTRL_ID_PANEL_STOP:
15767       if (game_status == GAME_MODE_MAIN)
15768         break;
15769
15770       if (tape.playing)
15771         TapeStop();
15772       else
15773         RequestQuitGame(TRUE);
15774
15775       break;
15776
15777     case GAME_CTRL_ID_PAUSE:
15778     case GAME_CTRL_ID_PAUSE2:
15779     case GAME_CTRL_ID_PANEL_PAUSE:
15780       if (network.enabled && game_status == GAME_MODE_PLAYING)
15781       {
15782 #if defined(NETWORK_AVALIABLE)
15783         if (tape.pausing)
15784           SendToServer_ContinuePlaying();
15785         else
15786           SendToServer_PausePlaying();
15787 #endif
15788       }
15789       else
15790         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15791
15792       game_undo_executed = FALSE;
15793
15794       break;
15795
15796     case GAME_CTRL_ID_PLAY:
15797     case GAME_CTRL_ID_PANEL_PLAY:
15798       if (game_status == GAME_MODE_MAIN)
15799       {
15800         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15801       }
15802       else if (tape.pausing)
15803       {
15804 #if defined(NETWORK_AVALIABLE)
15805         if (network.enabled)
15806           SendToServer_ContinuePlaying();
15807         else
15808 #endif
15809           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15810       }
15811       break;
15812
15813     case GAME_CTRL_ID_UNDO:
15814       // Important: When using "save snapshot when collecting an item" mode,
15815       // load last (current) snapshot for first "undo" after pressing "pause"
15816       // (else the last-but-one snapshot would be loaded, because the snapshot
15817       // pointer already points to the last snapshot when pressing "pause",
15818       // which is fine for "every step/move" mode, but not for "every collect")
15819       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15820           !game_undo_executed)
15821         steps--;
15822
15823       game_undo_executed = TRUE;
15824
15825       GameUndo(steps);
15826       break;
15827
15828     case GAME_CTRL_ID_REDO:
15829       GameRedo(steps);
15830       break;
15831
15832     case GAME_CTRL_ID_SAVE:
15833       TapeQuickSave();
15834       break;
15835
15836     case GAME_CTRL_ID_LOAD:
15837       TapeQuickLoad();
15838       break;
15839
15840     case SOUND_CTRL_ID_MUSIC:
15841     case SOUND_CTRL_ID_PANEL_MUSIC:
15842       if (setup.sound_music)
15843       { 
15844         setup.sound_music = FALSE;
15845
15846         FadeMusic();
15847       }
15848       else if (audio.music_available)
15849       { 
15850         setup.sound = setup.sound_music = TRUE;
15851
15852         SetAudioMode(setup.sound);
15853
15854         if (game_status == GAME_MODE_PLAYING)
15855           PlayLevelMusic();
15856       }
15857
15858       RedrawSoundButtonGadget(id);
15859
15860       break;
15861
15862     case SOUND_CTRL_ID_LOOPS:
15863     case SOUND_CTRL_ID_PANEL_LOOPS:
15864       if (setup.sound_loops)
15865         setup.sound_loops = FALSE;
15866       else if (audio.loops_available)
15867       {
15868         setup.sound = setup.sound_loops = TRUE;
15869
15870         SetAudioMode(setup.sound);
15871       }
15872
15873       RedrawSoundButtonGadget(id);
15874
15875       break;
15876
15877     case SOUND_CTRL_ID_SIMPLE:
15878     case SOUND_CTRL_ID_PANEL_SIMPLE:
15879       if (setup.sound_simple)
15880         setup.sound_simple = FALSE;
15881       else if (audio.sound_available)
15882       {
15883         setup.sound = setup.sound_simple = TRUE;
15884
15885         SetAudioMode(setup.sound);
15886       }
15887
15888       RedrawSoundButtonGadget(id);
15889
15890       break;
15891
15892     default:
15893       break;
15894   }
15895 }
15896
15897 static void HandleGameButtons(struct GadgetInfo *gi)
15898 {
15899   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15900 }
15901
15902 void HandleSoundButtonKeys(Key key)
15903 {
15904   if (key == setup.shortcut.sound_simple)
15905     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15906   else if (key == setup.shortcut.sound_loops)
15907     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15908   else if (key == setup.shortcut.sound_music)
15909     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15910 }